Kea: multi_threading_mgr.cc Source File

| Kea 2.2.0

multi_threading_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <util/multi_threading_mgr.h>
10
11namespace isc {
12namespace util {
13
14MultiThreadingMgr::MultiThreadingMgr()
15 : enabled_(false), critical_section_count_(0), thread_pool_size_(0) {
16}
17
18MultiThreadingMgr::~MultiThreadingMgr() {
19}
20
21 MultiThreadingMgr&
22MultiThreadingMgr::instance() {
23 static MultiThreadingMgr manager;
24 return (manager);
25}
26
27bool
28MultiThreadingMgr::getMode() const {
29 return (enabled_);
30}
31
32void
33MultiThreadingMgr::setMode(bool enabled) {
34 enabled_ = enabled;
35}
36
37void
38MultiThreadingMgr::enterCriticalSection() {
39 checkCallbacksPermissions();
40 bool inside = isInCriticalSection();
41 // Increment the counter to allow CS to be created in the registered
42 // callbacks (in which case the new CS would not call callbacks again).
43 // The counter must be updated regardless of the MT mode because the MT mode
44 // can change between the constructor call and the destructor call.
45 ++critical_section_count_;
46 if (getMode() && !inside) {
47 if (getThreadPoolSize()) {
48 thread_pool_.stop();
49 }
50 // Now it is safe to call callbacks which can also create other CSs.
51 callEntryCallbacks();
52 }
53}
54
55void
56MultiThreadingMgr::exitCriticalSection() {
57 // The number of CS destructors should match the number of CS constructors.
58 // The case when counter is 0 is only possible if calling this function
59 // explicitly, which is a programming error.
60 if (!isInCriticalSection()) {
61 isc_throw(InvalidOperation, "invalid value for critical section count");
62 }
63 // Decrement the counter to allow the check for last CS destructor which
64 // would result in restarting the thread pool.
65 // The counter must be updated regardless of the MT mode because the MT mode
66 // can change between the constructor call and the destructor call.
67 --critical_section_count_;
68 if (getMode() && !isInCriticalSection()) {
69 if (getThreadPoolSize()) {
70 thread_pool_.start(getThreadPoolSize());
71 }
72 // Now it is safe to call callbacks which can also create other CSs.
73 callExitCallbacks();
74 }
75}
76
77bool
78MultiThreadingMgr::isInCriticalSection() {
79 return (critical_section_count_ != 0);
80}
81
82 ThreadPool<std::function<void()>>&
83MultiThreadingMgr::getThreadPool() {
84 return thread_pool_;
85}
86
87uint32_t
88MultiThreadingMgr::getThreadPoolSize() const {
89 return (thread_pool_size_);
90}
91
92void
93MultiThreadingMgr::setThreadPoolSize(uint32_t size) {
94 thread_pool_size_ = size;
95}
96
97uint32_t
98MultiThreadingMgr::getPacketQueueSize() {
99 return (thread_pool_.getMaxQueueSize());
100}
101
102void
103MultiThreadingMgr::setPacketQueueSize(uint32_t size) {
104 thread_pool_.setMaxQueueSize(size);
105}
106
107uint32_t
108MultiThreadingMgr::detectThreadCount() {
109 return (std::thread::hardware_concurrency());
110}
111
112void
113MultiThreadingMgr::apply(bool enabled, uint32_t thread_count, uint32_t queue_size) {
114 // check the enabled flag
115 if (enabled) {
116 // check for auto scaling (enabled flag true but thread_count 0)
117 if (!thread_count) {
118 // might also return 0
119 thread_count = MultiThreadingMgr::detectThreadCount();
120 }
121 } else {
122 thread_count = 0;
123 queue_size = 0;
124 }
125 // check enabled flag and explicit number of threads or system supports
126 // hardware concurrency
127 if (thread_count) {
128 if (thread_pool_.size()) {
129 thread_pool_.stop();
130 }
131 setThreadPoolSize(thread_count);
132 setPacketQueueSize(queue_size);
133 setMode(true);
134 if (!isInCriticalSection()) {
135 thread_pool_.start(thread_count);
136 }
137 } else {
138 removeAllCriticalSectionCallbacks();
139 thread_pool_.reset();
140 setMode(false);
141 setThreadPoolSize(thread_count);
142 setPacketQueueSize(queue_size);
143 }
144}
145
146void
147MultiThreadingMgr::checkCallbacksPermissions() {
148 if (getMode()) {
149 for (const auto& cb : cs_callbacks_.getCallbackSets()) {
150 try {
151 (cb.check_cb_)();
152 } catch (const isc::MultiThreadingInvalidOperation& ex) {
153 // If any registered callback throws, the exception needs to be
154 // propagated to the caller of the
155 // @ref MultiThreadingCriticalSection constructor.
156 // Because this function is called by the
157 // @ref MultiThreadingCriticalSection constructor, throwing here
158 // is safe.
159 throw;
160 } catch (...) {
161 // We can't log it and throwing could be chaos.
162 // We'll swallow it and tell people their callbacks
163 // must be exception-proof
164 }
165 }
166 }
167}
168
169void
170MultiThreadingMgr::callEntryCallbacks() {
171 if (getMode()) {
172 const auto& callbacks = cs_callbacks_.getCallbackSets();
173 for (auto cb_it = callbacks.begin(); cb_it != callbacks.end(); cb_it++) {
174 try {
175 (cb_it->entry_cb_)();
176 } catch (...) {
177 // We can't log it and throwing could be chaos.
178 // We'll swallow it and tell people their callbacks
179 // must be exception-proof
180 }
181 }
182 }
183}
184
185void
186MultiThreadingMgr::callExitCallbacks() {
187 if (getMode()) {
188 const auto& callbacks = cs_callbacks_.getCallbackSets();
189 for (auto cb_it = callbacks.rbegin(); cb_it != callbacks.rend(); cb_it++) {
190 try {
191 (cb_it->exit_cb_)();
192 } catch (...) {
193 // We can't log it and throwing could be chaos.
194 // We'll swallow it and tell people their callbacks
195 // must be exception-proof
196 // Because this function is called by the
197 // @ref MultiThreadingCriticalSection destructor, throwing here
198 // is not safe and will cause the process to crash.
199 }
200 }
201 }
202}
203
204void
205MultiThreadingMgr::addCriticalSectionCallbacks(const std::string& name,
206 const CSCallbackSet::Callback& check_cb,
207 const CSCallbackSet::Callback& entry_cb,
208 const CSCallbackSet::Callback& exit_cb) {
209 cs_callbacks_.addCallbackSet(name, check_cb, entry_cb, exit_cb);
210}
211
212void
213MultiThreadingMgr::removeCriticalSectionCallbacks(const std::string& name) {
214 cs_callbacks_.removeCallbackSet(name);
215}
216
217void
218MultiThreadingMgr::removeAllCriticalSectionCallbacks() {
219 cs_callbacks_.removeAll();
220}
221
222MultiThreadingCriticalSection::MultiThreadingCriticalSection() {
223 MultiThreadingMgr::instance().enterCriticalSection();
224}
225
226MultiThreadingCriticalSection::~MultiThreadingCriticalSection() {
227 MultiThreadingMgr::instance().exitCriticalSection();
228}
229
230MultiThreadingLock::MultiThreadingLock(std::mutex& mutex) {
231 if (MultiThreadingMgr::instance().getMode()) {
232 lock_ = std::unique_lock<std::mutex>(mutex);
233 }
234}
235
236void
237CSCallbackSetList::addCallbackSet(const std::string& name,
238 const CSCallbackSet::Callback& check_cb,
239 const CSCallbackSet::Callback& entry_cb,
240 const CSCallbackSet::Callback& exit_cb) {
241 if (name.empty()) {
242 isc_throw(BadValue, "CSCallbackSetList - name cannot be empty");
243 }
244
245 if (!check_cb) {
246 isc_throw(BadValue, "CSCallbackSetList - check callback for " << name
247 << " cannot be empty");
248 }
249
250 if (!entry_cb) {
251 isc_throw(BadValue, "CSCallbackSetList - entry callback for " << name
252 << " cannot be empty");
253 }
254
255 if (!exit_cb) {
256 isc_throw(BadValue, "CSCallbackSetList - exit callback for " << name
257 << " cannot be empty");
258 }
259
260 for (auto const& callback : cb_sets_) {
261 if (callback.name_ == name) {
262 isc_throw(BadValue, "CSCallbackSetList - callbacks for " << name
263 << " already exist");
264 }
265 }
266
267 cb_sets_.push_back(CSCallbackSet(name, check_cb, entry_cb, exit_cb));
268}
269
270void
271CSCallbackSetList::removeCallbackSet(const std::string& name) {
272 for (auto it = cb_sets_.begin(); it != cb_sets_.end(); ++it) {
273 if ((*it).name_ == name) {
274 cb_sets_.erase(it);
275 break;
276 }
277 }
278}
279
280void
281CSCallbackSetList::removeAll() {
282 cb_sets_.clear();
283}
284
285const std::list<CSCallbackSet>&
286CSCallbackSetList::getCallbackSets() {
287 return (cb_sets_);
288}
289
290} // namespace util
291} // namespace isc
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::InvalidOperation
A generic exception that is thrown if a function is called in a prohibited way.
Definition: exceptions/exceptions.h:143
isc::MultiThreadingInvalidOperation
Exception thrown when a worker thread is trying to stop or pause the respective thread pool (which wo...
Definition: exceptions/exceptions.h:183
isc::util::CSCallbackSetList::removeCallbackSet
void removeCallbackSet(const std::string &name)
Removes a callback set from the list.
Definition: multi_threading_mgr.cc:271
isc::util::CSCallbackSetList::getCallbackSets
const std::list< CSCallbackSet > & getCallbackSets()
Fetches the list of callback sets.
Definition: multi_threading_mgr.cc:286
isc::util::CSCallbackSetList::removeAll
void removeAll()
Removes all callbacks from the list.
Definition: multi_threading_mgr.cc:281
isc::util::CSCallbackSetList::addCallbackSet
void addCallbackSet(const std::string &name, const CSCallbackSet::Callback &check_cb, const CSCallbackSet::Callback &entry_cb, const CSCallbackSet::Callback &exit_cb)
Adds a callback set to the list.
Definition: multi_threading_mgr.cc:237
isc::util::MultiThreadingCriticalSection::MultiThreadingCriticalSection
MultiThreadingCriticalSection()
Constructor.
Definition: multi_threading_mgr.cc:222
isc::util::MultiThreadingCriticalSection::~MultiThreadingCriticalSection
virtual ~MultiThreadingCriticalSection()
Destructor.
Definition: multi_threading_mgr.cc:226
isc::util::MultiThreadingMgr
Multi Threading Manager.
Definition: multi_threading_mgr.h:136
isc::util::MultiThreadingMgr::removeAllCriticalSectionCallbacks
void removeAllCriticalSectionCallbacks()
Removes all callbacks in the list of CriticalSection callbacks.
Definition: multi_threading_mgr.cc:218
isc::util::MultiThreadingMgr::setMode
void setMode(bool enabled)
Set the multi-threading mode.
Definition: multi_threading_mgr.cc:33
isc::util::MultiThreadingMgr::~MultiThreadingMgr
virtual ~MultiThreadingMgr()
Destructor.
Definition: multi_threading_mgr.cc:18
isc::util::MultiThreadingMgr::MultiThreadingMgr
MultiThreadingMgr()
Constructor.
Definition: multi_threading_mgr.cc:14
isc::util::MultiThreadingMgr::instance
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
Definition: multi_threading_mgr.cc:22
isc::util::MultiThreadingMgr::enterCriticalSection
void enterCriticalSection()
Enter critical section.
Definition: multi_threading_mgr.cc:38
isc::util::MultiThreadingMgr::setThreadPoolSize
void setThreadPoolSize(uint32_t size)
Set the configured dhcp thread pool size.
Definition: multi_threading_mgr.cc:93
isc::util::MultiThreadingMgr::getPacketQueueSize
uint32_t getPacketQueueSize()
Get the configured dhcp packet queue size.
Definition: multi_threading_mgr.cc:98
isc::util::MultiThreadingMgr::removeCriticalSectionCallbacks
void removeCriticalSectionCallbacks(const std::string &name)
Removes the set of callbacks associated with a given name from the list of CriticalSection callbacks.
Definition: multi_threading_mgr.cc:213
isc::util::MultiThreadingMgr::setPacketQueueSize
void setPacketQueueSize(uint32_t size)
Set the configured dhcp packet queue size.
Definition: multi_threading_mgr.cc:103
isc::util::MultiThreadingMgr::getThreadPoolSize
uint32_t getThreadPoolSize() const
Get the configured dhcp thread pool size.
Definition: multi_threading_mgr.cc:88
isc::util::MultiThreadingMgr::getThreadPool
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
Definition: multi_threading_mgr.cc:83
isc::util::MultiThreadingMgr::isInCriticalSection
bool isInCriticalSection()
Is in critical section flag.
Definition: multi_threading_mgr.cc:78
isc::util::MultiThreadingMgr::detectThreadCount
static uint32_t detectThreadCount()
The system current detected hardware concurrency thread count.
Definition: multi_threading_mgr.cc:108
isc::util::MultiThreadingMgr::apply
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
Definition: multi_threading_mgr.cc:113
isc::util::MultiThreadingMgr::getMode
bool getMode() const
Get the multi-threading mode.
Definition: multi_threading_mgr.cc:28
isc::util::MultiThreadingMgr::addCriticalSectionCallbacks
void addCriticalSectionCallbacks(const std::string &name, const CSCallbackSet::Callback &check_cb, const CSCallbackSet::Callback &entry_cb, const CSCallbackSet::Callback &exit_cb)
Adds a set of callbacks to the list of CriticalSection callbacks.
Definition: multi_threading_mgr.cc:205
isc::util::MultiThreadingMgr::exitCriticalSection
void exitCriticalSection()
Exit critical section.
Definition: multi_threading_mgr.cc:56
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:210
multi_threading_mgr.h
isc
Defines the logger used by the top-level component of kea-lfc.
Definition: agent_parser.cc:143
isc::util::CSCallbackSet
Embodies a named set of CriticalSection callbacks.
Definition: multi_threading_mgr.h:36
isc::util::CSCallbackSet::Callback
std::function< void()> Callback
Defines a callback as a simple void() functor.
Definition: multi_threading_mgr.h:38
isc::util::MultiThreadingLock::MultiThreadingLock
MultiThreadingLock(std::mutex &mutex)
Constructor locks the mutex if multi-threading is enabled.
Definition: multi_threading_mgr.cc:230
isc::util::ThreadPool
Defines a thread pool which uses a thread pool queue for managing work items.
Definition: thread_pool.h:34
isc::util::ThreadPool::setMaxQueueSize
void setMaxQueueSize(size_t max_queue_size)
set maximum number of work items in the queue
Definition: thread_pool.h:146
isc::util::ThreadPool::reset
void reset()
reset the thread pool stopping threads and clearing the internal queue
Definition: thread_pool.h:60
isc::util::ThreadPool::start
void start(uint32_t thread_count)
start all the threads
Definition: thread_pool.h:72
isc::util::ThreadPool::getMaxQueueSize
size_t getMaxQueueSize()
get maximum number of work items in the queue
Definition: thread_pool.h:153
isc::util::ThreadPool::size
size_t size()
size number of thread pool threads
Definition: thread_pool.h:160
isc::util::ThreadPool::stop
void stop()
stop all the threads
Definition: thread_pool.h:85