RESTinio

connection_count_limiter.hpp
Go to the documentation of this file.
1/*
2 * RESTinio
3 */
4
11#pragma once
12
13#include <restinio/null_mutex.hpp>
14#include <restinio/default_strands.hpp>
15
16#include <restinio/utils/tagged_scalar.hpp>
17
18#include <cstdint>
19#include <mutex>
20#include <utility>
21
22 namespace restinio
23{
24
25 namespace connection_count_limits
26{
27
28//
29// max_parallel_connections_t
30//
31 struct max_parallel_connections_tag {};
32
38 using max_parallel_connections_t = restinio::utils::tagged_scalar_t<
39 std::size_t, max_parallel_connections_tag >;
40
41//
42// max_active_accepts_t
43//
44 struct max_active_accepts_tag {};
45
51 using max_active_accepts_t = restinio::utils::tagged_scalar_t<
52 std::size_t, max_active_accepts_tag >;
53
54 namespace impl
55{
56
77 class acceptor_callback_iface_t
78{
79 public:
84 virtual void
85 call_accept_now(
87 std::size_t index ) noexcept = 0;
88
97 virtual void
98 schedule_next_accept_attempt(
100 std::size_t index ) noexcept = 0;
101};
102
114 template< typename Mutex_Type >
115 class actual_limiter_t
116{
118 Mutex_Type m_lock;
119
121 not_null_pointer_t< acceptor_callback_iface_t > m_acceptor;
122
138 std::size_t m_active_accepts{ 0u };
139
146 std::size_t m_connections{ 0u };
147
149 const std::size_t m_max_parallel_connections;
150
165 std::vector< std::size_t > m_pending_indexes;
166
167 RESTINIO_NODISCARD
168 bool
169 has_free_slots() const noexcept
170 {
171 return (m_active_accepts + m_connections) < m_max_parallel_connections;
172 }
173
174 public:
175 actual_limiter_t(
176 not_null_pointer_t< acceptor_callback_iface_t > acceptor,
177 max_parallel_connections_t max_parallel_connections,
178 max_active_accepts_t max_pending_indexes )
179 : m_acceptor{ acceptor }
180 , m_max_parallel_connections{ max_parallel_connections.value() }
181 {
182 m_pending_indexes.reserve( max_pending_indexes.value() );
183 }
184
185 actual_limiter_t( const actual_limiter_t & ) = delete;
186 actual_limiter_t( actual_limiter_t && ) = delete;
187
188 void
189 increment_parallel_connections() noexcept
190 {
191 std::lock_guard< Mutex_Type > lock{ m_lock };
192
193 // Expects that m_active_accepts is always greater than 0.
194 --m_active_accepts;
195
196 ++m_connections;
197 }
198
199 // Note: this method is noexcept because it can be called from
200 // destructors.
201 void
202 decrement_parallel_connections() noexcept
203 {
204 // Decrement active connections under acquired lock.
205 // If the count of connections drops below the limit and
206 // there are some pending indexes then one of them will
207 // be returned (wrapped into an optional).
208 auto index_to_activate = [this]() -> optional_t<std::size_t> {
209 std::lock_guard< Mutex_Type > lock{ m_lock };
210
211 // Expects that m_connections is always greater than 0.
212 --m_connections;
213
214 if( has_free_slots() && !m_pending_indexes.empty() )
215 {
216 std::size_t pending_index = m_pending_indexes.back();
217 m_pending_indexes.pop_back();
218 return pending_index;
219 }
220 else
221 return nullopt;
222 }();
223
224 if( index_to_activate )
225 {
226 m_acceptor->schedule_next_accept_attempt( *index_to_activate );
227 }
228 }
229
235 void
236 accept_next( std::size_t index ) noexcept
237 {
238 // Perform all operations under acquired lock.
239 // The result is a flag that tells can accept() be called right now.
240 const bool accept_now = [this, index]() -> bool {
241 std::lock_guard< Mutex_Type > lock{ m_lock };
242
243 if( has_free_slots() )
244 {
245 ++m_active_accepts;
246 return true;
247 }
248 else
249 {
250 m_pending_indexes.push_back( index );
251 return false;
252 }
253 }();
254
255 if( accept_now )
256 {
257 m_acceptor->call_accept_now( index );
258 }
259 }
260};
261
262} /* namespace impl */
263
270 class noop_connection_count_limiter_t
271{
272 not_null_pointer_t< connection_count_limits::impl::acceptor_callback_iface_t > m_acceptor;
273
274 public:
275 noop_connection_count_limiter_t(
276 not_null_pointer_t< connection_count_limits::impl::acceptor_callback_iface_t > acceptor,
277 max_parallel_connections_t /*max_parallel_connections*/,
278 max_active_accepts_t /*max_pending_indexes*/ )
279 : m_acceptor{ acceptor }
280 {
281 }
282
283 void
284 increment_parallel_connections() noexcept { /* Nothing to do */ }
285
286 void
287 decrement_parallel_connections() noexcept { /* Nothing to do */ }
288
293 void
294 accept_next( std::size_t index ) noexcept
295 {
296 m_acceptor->call_accept_now( index );
297 }
298};
299
309 template< typename Strand >
310 class connection_count_limiter_t;
311
321 template<>
322 class connection_count_limiter_t< noop_strand_t >
323 : public connection_count_limits::impl::actual_limiter_t< null_mutex_t >
324{
325 using base_t = connection_count_limits::impl::actual_limiter_t< null_mutex_t >;
326
327 public:
328 using base_t::base_t;
329};
330
340 template<>
341 class connection_count_limiter_t< default_strand_t >
342 : public connection_count_limits::impl::actual_limiter_t< std::mutex >
343{
344 using base_t = connection_count_limits::impl::actual_limiter_t< std::mutex >;
345
346 public:
347 using base_t::base_t;
348};
349
373 template< typename Count_Manager >
374 class connection_lifetime_monitor_t
375{
376 not_null_pointer_t< Count_Manager > m_manager;
377
378 public:
379 connection_lifetime_monitor_t(
380 not_null_pointer_t< Count_Manager > manager ) noexcept
381 : m_manager{ manager }
382 {
383 m_manager->increment_parallel_connections();
384 }
385
386 ~connection_lifetime_monitor_t()
387 {
388 if( m_manager )
389 m_manager->decrement_parallel_connections();
390 }
391
392 connection_lifetime_monitor_t(
393 const connection_lifetime_monitor_t & ) = delete;
394
395 friend void
396 swap(
397 connection_lifetime_monitor_t & a,
398 connection_lifetime_monitor_t & b ) noexcept
399 {
400 using std::swap;
401 swap( a.m_manager, b.m_manager );
402 }
403
404 connection_lifetime_monitor_t(
405 connection_lifetime_monitor_t && other ) noexcept
406 : m_manager{ other.m_manager }
407 {
408 other.m_manager = nullptr;
409 }
410
411 connection_lifetime_monitor_t &
412 operator=( connection_lifetime_monitor_t && other ) noexcept
413 {
414 connection_lifetime_monitor_t tmp{ std::move(other) };
415 swap( *this, tmp );
416 return *this;
417 }
418
419 connection_lifetime_monitor_t &
420 operator=( const connection_lifetime_monitor_t & ) = delete;
421};
422
431 template<>
432 class connection_lifetime_monitor_t< noop_connection_count_limiter_t >
433{
434 public:
435 connection_lifetime_monitor_t(
436 not_null_pointer_t< noop_connection_count_limiter_t > ) noexcept
437 {}
438};
439
440} /* namespace connection_count_limits */
441
457 template< typename Traits >
458 struct connection_count_limit_types
459{
460 using limiter_t = typename std::conditional
461 <
462 Traits::use_connection_count_limiter,
463 connection_count_limits::connection_count_limiter_t<
464 typename Traits::strand_t >,
465 connection_count_limits::noop_connection_count_limiter_t
466 >::type;
467
468 using lifetime_monitor_t =
469 connection_count_limits::connection_lifetime_monitor_t< limiter_t >;
470};
471
472} /* namespace restinio */
473
nonstd::optional_lite::optional
class optional
Definition: optional.hpp:919
restinio::connection_count_limits::connection_count_limiter_t
Template class for connection count limiter for the case when connection count limit is actually used...
Definition: connection_count_limiter.hpp:310
restinio::connection_count_limits::connection_lifetime_monitor_t< noop_connection_count_limiter_t >::connection_lifetime_monitor_t
connection_lifetime_monitor_t(not_null_pointer_t< noop_connection_count_limiter_t >) noexcept
Definition: connection_count_limiter.hpp:435
restinio::connection_count_limits::connection_lifetime_monitor_t
Helper type for controlling the lifetime of the connection.
Definition: connection_count_limiter.hpp:375
restinio::connection_count_limits::connection_lifetime_monitor_t::operator=
connection_lifetime_monitor_t & operator=(connection_lifetime_monitor_t &&other) noexcept
Definition: connection_count_limiter.hpp:412
restinio::connection_count_limits::connection_lifetime_monitor_t::~connection_lifetime_monitor_t
~connection_lifetime_monitor_t()
Definition: connection_count_limiter.hpp:386
restinio::connection_count_limits::connection_lifetime_monitor_t::swap
friend void swap(connection_lifetime_monitor_t &a, connection_lifetime_monitor_t &b) noexcept
Definition: connection_count_limiter.hpp:396
restinio::connection_count_limits::connection_lifetime_monitor_t::connection_lifetime_monitor_t
connection_lifetime_monitor_t(connection_lifetime_monitor_t &&other) noexcept
Definition: connection_count_limiter.hpp:404
restinio::connection_count_limits::connection_lifetime_monitor_t::operator=
connection_lifetime_monitor_t & operator=(const connection_lifetime_monitor_t &)=delete
restinio::connection_count_limits::connection_lifetime_monitor_t::connection_lifetime_monitor_t
connection_lifetime_monitor_t(const connection_lifetime_monitor_t &)=delete
restinio::connection_count_limits::connection_lifetime_monitor_t::m_manager
not_null_pointer_t< Count_Manager > m_manager
Definition: connection_count_limiter.hpp:376
restinio::connection_count_limits::connection_lifetime_monitor_t::connection_lifetime_monitor_t
connection_lifetime_monitor_t(not_null_pointer_t< Count_Manager > manager) noexcept
Definition: connection_count_limiter.hpp:379
restinio::connection_count_limits::impl::acceptor_callback_iface_t
An interface of acceptor to be used by connection count limiters.
Definition: connection_count_limiter.hpp:78
restinio::connection_count_limits::impl::acceptor_callback_iface_t::schedule_next_accept_attempt
virtual void schedule_next_accept_attempt(std::size_t index) noexcept=0
restinio::connection_count_limits::impl::acceptor_callback_iface_t::call_accept_now
virtual void call_accept_now(std::size_t index) noexcept=0
restinio::connection_count_limits::impl::actual_limiter_t
Actual implementation of connection count limiter.
Definition: connection_count_limiter.hpp:116
restinio::connection_count_limits::impl::actual_limiter_t::m_pending_indexes
std::vector< std::size_t > m_pending_indexes
The storage for holding pending socket's slots.
Definition: connection_count_limiter.hpp:165
restinio::connection_count_limits::impl::actual_limiter_t::m_active_accepts
std::size_t m_active_accepts
The counter of active accept() operations.
Definition: connection_count_limiter.hpp:138
restinio::connection_count_limits::impl::actual_limiter_t::m_lock
Mutex_Type m_lock
Lock object to be used.
Definition: connection_count_limiter.hpp:118
restinio::connection_count_limits::impl::actual_limiter_t::actual_limiter_t
actual_limiter_t(actual_limiter_t &&)=delete
restinio::connection_count_limits::impl::actual_limiter_t::m_max_parallel_connections
const std::size_t m_max_parallel_connections
The limit for parallel connections.
Definition: connection_count_limiter.hpp:149
restinio::connection_count_limits::impl::actual_limiter_t::has_free_slots
RESTINIO_NODISCARD bool has_free_slots() const noexcept
Definition: connection_count_limiter.hpp:169
restinio::connection_count_limits::impl::actual_limiter_t::m_connections
std::size_t m_connections
The counter of active connections.
Definition: connection_count_limiter.hpp:146
restinio::connection_count_limits::impl::actual_limiter_t::actual_limiter_t
actual_limiter_t(not_null_pointer_t< acceptor_callback_iface_t > acceptor, max_parallel_connections_t max_parallel_connections, max_active_accepts_t max_pending_indexes)
Definition: connection_count_limiter.hpp:175
restinio::connection_count_limits::impl::actual_limiter_t::actual_limiter_t
actual_limiter_t(const actual_limiter_t &)=delete
restinio::connection_count_limits::impl::actual_limiter_t::decrement_parallel_connections
void decrement_parallel_connections() noexcept
Definition: connection_count_limiter.hpp:202
restinio::connection_count_limits::impl::actual_limiter_t::increment_parallel_connections
void increment_parallel_connections() noexcept
Definition: connection_count_limiter.hpp:189
restinio::connection_count_limits::impl::actual_limiter_t::m_acceptor
not_null_pointer_t< acceptor_callback_iface_t > m_acceptor
Mandatory pointer to the acceptor connected with this limiter.
Definition: connection_count_limiter.hpp:121
restinio::connection_count_limits::impl::actual_limiter_t::accept_next
void accept_next(std::size_t index) noexcept
Definition: connection_count_limiter.hpp:236
restinio::connection_count_limits::noop_connection_count_limiter_t
An implementation of connection count limiter for the case when connection count is not limited.
Definition: connection_count_limiter.hpp:271
restinio::connection_count_limits::noop_connection_count_limiter_t::noop_connection_count_limiter_t
noop_connection_count_limiter_t(not_null_pointer_t< connection_count_limits::impl::acceptor_callback_iface_t > acceptor, max_parallel_connections_t, max_active_accepts_t)
Definition: connection_count_limiter.hpp:275
restinio::connection_count_limits::noop_connection_count_limiter_t::accept_next
void accept_next(std::size_t index) noexcept
Definition: connection_count_limiter.hpp:294
restinio::connection_count_limits::noop_connection_count_limiter_t::increment_parallel_connections
void increment_parallel_connections() noexcept
Definition: connection_count_limiter.hpp:284
restinio::connection_count_limits::noop_connection_count_limiter_t::decrement_parallel_connections
void decrement_parallel_connections() noexcept
Definition: connection_count_limiter.hpp:287
restinio::connection_count_limits::noop_connection_count_limiter_t::m_acceptor
not_null_pointer_t< connection_count_limits::impl::acceptor_callback_iface_t > m_acceptor
Definition: connection_count_limiter.hpp:272
restinio::utils::tagged_scalar_t
Helper template for defining tagged scalar types.
Definition: tagged_scalar.hpp:54
restinio::utils::tagged_scalar_t::value
RESTINIO_NODISCARD constexpr Scalar value() const noexcept
Definition: tagged_scalar.hpp:67
RESTINIO_NODISCARD
#define RESTINIO_NODISCARD
Definition: compiler_features.hpp:33
default_strands.hpp
Typedefs for default strands.
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
nonstd::optional_lite::nullopt
const nullopt_t nullopt((nullopt_t::init()))
nonstd::optional_lite::swap
void swap(optional< T > &x, optional< T > &y)
Definition: optional.hpp:1705
restinio
Definition: asio_include.hpp:21
restinio::default_strand_t
asio_ns::strand< default_asio_executor > default_strand_t
A typedef for the default strand type.
Definition: default_strands.hpp:23
restinio::not_null_pointer_t
T * not_null_pointer_t
Type for pointer that is not null by design.
Definition: common_types.hpp:169
restinio::noop_strand_t
default_asio_executor noop_strand_t
A typedef for no-op strand type.
Definition: default_strands.hpp:28
null_mutex.hpp
Definition of null_mutex.
restinio::connection_count_limit_types
A kind of metafunction that deduces actual types related to connection count limiter in the dependecy...
Definition: connection_count_limiter.hpp:459
restinio::connection_count_limit_types::limiter_t
typename std::conditional< Traits::use_connection_count_limiter, connection_count_limits::connection_count_limiter_t< typename Traits::strand_t >, connection_count_limits::noop_connection_count_limiter_t >::type limiter_t
Definition: connection_count_limiter.hpp:466
restinio::connection_count_limits::max_active_accepts_tag
Definition: connection_count_limiter.hpp:44
restinio::connection_count_limits::max_parallel_connections_tag
Definition: connection_count_limiter.hpp:31
tagged_scalar.hpp
Helper template for defining tagged scalar types.
const
#define const
Definition: zconf.h:230