RESTinio
connection_count_limiter.hpp
Go to the documentation of this file.
1/*
2 * RESTinio
3 */
4
11#pragma once
12
15
17
18#include <cstdint>
19#include <mutex>
20#include <utility>
21
22namespace restinio
23{
24
25namespace connection_count_limits
26{
27
28//
29// max_parallel_connections_t
30//
32
39 std::size_t, max_parallel_connections_tag >;
40
41//
42// max_active_accepts_t
43//
45
52 std::size_t, max_active_accepts_tag >;
53
54namespace impl
55{
56
78{
79public:
84 virtual void
87 std::size_t index ) noexcept = 0;
88
97 virtual void
100 std::size_t index ) noexcept = 0;
101};
102
114template< typename Mutex_Type >
116{
118 Mutex_Type m_lock;
119
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
168 bool
170 {
172 }
173
174public:
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
187
188 void
190 {
191 std::lock_guard< Mutex_Type > lock{ m_lock };
192
193 // Expects that m_active_accepts is always greater than 0.
195
197 }
198
199 // Note: this method is noexcept because it can be called from
200 // destructors.
201 void
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.
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 {
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
271{
273
274public:
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
309template< typename Strand >
311
321template<>
324{
326
327public:
328 using base_t::base_t;
329};
330
340template<>
343{
345
346public:
347 using base_t::base_t;
348};
349
373template< typename Count_Manager >
375{
377
378public:
380 not_null_pointer_t< Count_Manager > manager ) noexcept
381 : m_manager{ manager }
382 {
383 m_manager->increment_parallel_connections();
384 }
385
387 {
388 if( m_manager )
389 m_manager->decrement_parallel_connections();
390 }
391
393 const connection_lifetime_monitor_t & ) = delete;
394
395 friend void
398 connection_lifetime_monitor_t & b ) noexcept
399 {
400 using std::swap;
401 swap( a.m_manager, b.m_manager );
402 }
403
405 connection_lifetime_monitor_t && other ) noexcept
406 : m_manager{ other.m_manager }
407 {
408 other.m_manager = nullptr;
409 }
410
413 {
415 swap( *this, tmp );
416 return *this;
417 }
418
421};
422
431template<>
433{
434public:
437 {}
438};
439
440} /* namespace connection_count_limits */
441
457template< typename Traits >
459{
460 using limiter_t = typename std::conditional
461 <
462 Traits::use_connection_count_limiter,
464 typename Traits::strand_t >,
466 >::type;
467
470};
471
472} /* namespace restinio */
473
Template class for connection count limiter for the case when connection count limit is actually used...
Helper type for controlling the lifetime of the connection.
connection_lifetime_monitor_t & operator=(connection_lifetime_monitor_t &&other) noexcept
friend void swap(connection_lifetime_monitor_t &a, connection_lifetime_monitor_t &b) noexcept
connection_lifetime_monitor_t(connection_lifetime_monitor_t &&other) noexcept
connection_lifetime_monitor_t & operator=(const connection_lifetime_monitor_t &)=delete
connection_lifetime_monitor_t(const connection_lifetime_monitor_t &)=delete
connection_lifetime_monitor_t(not_null_pointer_t< Count_Manager > manager) noexcept
An interface of acceptor to be used by connection count limiters.
virtual void schedule_next_accept_attempt(std::size_t index) noexcept=0
virtual void call_accept_now(std::size_t index) noexcept=0
Actual implementation of connection count limiter.
std::vector< std::size_t > m_pending_indexes
The storage for holding pending socket's slots.
std::size_t m_active_accepts
The counter of active accept() operations.
const std::size_t m_max_parallel_connections
The limit for parallel connections.
std::size_t m_connections
The counter of active connections.
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)
not_null_pointer_t< acceptor_callback_iface_t > m_acceptor
Mandatory pointer to the acceptor connected with this limiter.
An implementation of connection count limiter for the case when connection count is not limited.
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)
not_null_pointer_t< connection_count_limits::impl::acceptor_callback_iface_t > m_acceptor
Helper template for defining tagged scalar types.
RESTINIO_NODISCARD constexpr Scalar value() const noexcept
#define RESTINIO_NODISCARD
Typedefs for default strands.
const nullopt_t nullopt((nullopt_t::init()))
void swap(optional< T > &x, optional< T > &y)
Definition: optional.hpp:1705
asio_ns::strand< default_asio_executor > default_strand_t
A typedef for the default strand type.
T * not_null_pointer_t
Type for pointer that is not null by design.
default_asio_executor noop_strand_t
A typedef for no-op strand type.
Definition of null_mutex.
A kind of metafunction that deduces actual types related to connection count limiter in the dependecy...
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
Helper template for defining tagged scalar types.
#define const
Definition: zconf.h:230