SObjectizer  5.8
Loading...
Searching...
No Matches
quoted_round_robin/impl/demand_queue.hpp
Go to the documentation of this file.
1/*
2 SObjectizer 5.
3*/
4
5/*!
6 * \since
7 * v.5.5.8
8 *
9 * \file
10 * \brief A demand queue for dispatcher with one common working
11 * thread and round-robin processing of prioritised demand on
12 * quoted basic.
13 */
14
15#pragma once
16
17#include <memory>
18#include <atomic>
19
20#include <so_5/execution_demand.hpp>
21#include <so_5/event_queue.hpp>
22
23#include <so_5/priority.hpp>
24
25#include <so_5/disp/mpsc_queue_traits/pub.hpp>
26
27#include <so_5/disp/prio_one_thread/quoted_round_robin/quotes.hpp>
28
29#if defined(__clang__) && (__clang_major__ >= 16)
30#pragma clang diagnostic push
31#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
32#endif
33
34namespace so_5 {
35
36namespace disp {
37
38namespace prio_one_thread {
39
40namespace quoted_round_robin {
41
42namespace impl {
43
44namespace queue_traits = so_5::disp::mpsc_queue_traits;
45
46//
47// demand_t
48//
49/*!
50 * \since
51 * v.5.5.8
52 *
53 * \brief A single execution demand.
54 */
55struct demand_t final : public execution_demand_t
56 {
57 //! Next demand in the queue.
58 demand_t * m_next = nullptr;
59
60 //! Initializing constructor.
64 };
65
66//
67// demand_unique_ptr_t
68//
69/*!
70 * \since
71 * v.5.5.8
72 *
73 * \brief An alias for unique_ptr to demand.
74 */
76
77//
78// demand_queue_t
79//
80/*!
81 * \since
82 * v.5.5.8
83 *
84 * \brief A demand queue for dispatcher with one common working
85 * thread and round-robin processing of prioritised demand on
86 * quoted basic.
87 */
89 {
90 friend struct queue_for_one_priority_t;
91
92 //! Description of queue for one priority.
94 : public event_queue_t
95 {
96 //! Pointer to main demand queue.
98
99 //! Head of the queue.
100 /*! Null if queue is empty. */
101 demand_t * m_head = nullptr;
102 //! Tail of the queue.
103 /*! Null if queue is empty. */
104 demand_t * m_tail = nullptr;
105
106 //! A quote for this subqueue.
107 /*!
108 * \note Actual value will be set later in the constructor
109 * of demand_queue_t.
110 */
112 //! Count of processed demands on the current iterations.
114
115 /*!
116 * \name Information for run-time monitoring.
117 * \{
118 */
119 //! Count of agents attached to that queue.
121 //! Count of demands in the queue.
123 /*!
124 * \}
125 */
126
127 void
128 push( execution_demand_t exec_demand ) override
129 {
130 demand_unique_ptr_t what{ new demand_t{
131 std::move( exec_demand ) } };
132
133 m_demand_queue->push( this, std::move( what ) );
134 }
135
136 /*!
137 * \note
138 * Delegates the work to the push() method.
139 */
140 void
142 {
143 this->push( std::move(demand) );
144 }
145
146 /*!
147 * \note
148 * Delegates the work to the push() method.
149 *
150 * \attention
151 * Terminates the whole application if the push() throws.
152 */
153 void
154 push_evt_finish( execution_demand_t demand ) noexcept override
155 {
156 this->push( std::move(demand) );
157 }
158 };
159
160 public :
161 //! This exception is thrown when pop is called after stop.
163 {};
164
165 //! Statistic about one subqueue.
173
175 queue_traits::lock_unique_ptr_t lock,
176 const quotes_t & quotes )
177 : m_lock{ std::move(lock) }
180 {
181 so_5::prio::for_each_priority( [&]( priority_t p ) {
182 auto & q = m_priorities[ to_size_t(p) ];
183 // Every subqueue must have a valid pointer to main demand
184 // queue.
185 q.m_demand_queue = this;
186 // Quote for the subqueue must be defined.
187 q.m_quote = quotes.query( p );
188 } );
189 }
191 {
192 for( auto & q : m_priorities )
193 cleanup_queue( q );
194 }
195
196 //! Set the shutdown signal.
197 void
199 {
200 queue_traits::lock_guard_t lock{ *m_lock };
201
202 m_shutdown = true;
203
204 if( !m_total_demands_count )
205 // There could be a sleeping working thread.
206 // It must be notified.
207 lock.notify_one();
208 }
209
210 //! Pop demand from the queue.
211 /*!
212 * \throw shutdown_ex_t in the case when queue is shut down.
213 */
216 {
217 queue_traits::unique_lock_t lock{ *m_lock };
218
219 while( !m_shutdown && !m_total_demands_count )
220 lock.wait_for_notify();
221
222 if( m_shutdown )
223 throw shutdown_ex_t();
224
225 // Note: this loop should not be infinitife because
226 // m_total_demands_count is not a zero. It means that
227 // there is at least one demand somewhere.
230
231 // There is a demand to extract.
232 demand_unique_ptr_t result{ m_current_priority->m_head };
233
234 m_current_priority->m_head = result->m_next;
237
238 result->m_next = nullptr;
239
240 --(m_current_priority->m_demands_count);
241 --m_total_demands_count;
242
243 ++(m_current_priority->m_demands_processed);
244
245 if( m_current_priority->m_demands_processed >=
246 m_current_priority->m_quote )
247 {
248 // Processing of this priority on the current
249 // iteration is finished.
251 }
252
253 return result;
254 }
255
256 //! Get queue for the priority specified.
259 {
260 return m_priorities[ to_size_t(priority) ];
261 }
262
263 //! Notification about attachment of yet another agent to the queue.
264 void
266 {
267 ++(m_priorities[ to_size_t(priority) ].m_agents_count);
268 }
269
270 //! Notification about detachment of an agent from the queue.
271 void
273 {
274 --(m_priorities[ to_size_t(priority) ].m_agents_count);
275 }
276
277 //! A special method for handling statistical data for
278 //! every subqueue.
279 template< class Lambda >
280 void
291
292 private :
293 //! Queue lock.
295
296 //! Shutdown flag.
297 bool m_shutdown = false;
298
299 //! Total count of demands in the queue.
301
302 //! Subqueues for priorities.
304 static_cast< std::size_t >( priority_t::p_max ) + 1 ];
305
306 //! Pointer to the current subqueue.
308
309 //! Destroy all demands in the queue specified.
310 void
312 {
313 auto h = queue_info.m_head;
314 while( h )
315 {
316 demand_unique_ptr_t t{ h };
317 h = h->m_next;
318 }
319 }
320
321 //! Push a new demand to the queue.
322 void
324 //! Subqueue for the demand.
325 queue_for_one_priority_t * subqueue,
326 //! Demand to be pushed.
327 demand_unique_ptr_t demand )
328 {
329 queue_traits::lock_guard_t lock{ *m_lock };
330
331 add_demand_to_queue( *subqueue, std::move( demand ) );
332 ++m_total_demands_count;
333
334 if( 1 == m_total_demands_count )
335 // Queue was empty. A sleeping working thread must
336 // be notified.
337 lock.notify_one();
338 }
339
340 //! Add a new demand to the tail of the queue specified.
341 void
344 demand_unique_ptr_t demand )
345 {
346 if( queue.m_tail )
347 {
348 // Queue is not empty. Tail will be modified.
349 queue.m_tail->m_next = demand.release();
350 queue.m_tail = queue.m_tail->m_next;
351 }
352 else
353 {
354 // Queue is empty. The whole description will be modified.
355 queue.m_head = demand.release();
356 queue.m_tail = queue.m_head;
357 }
358
359 ++(queue.m_demands_count);
360 }
361
362 void
364 {
365 // Iteration on the current priority is finished.
366 // Count of processed demands must be started from zero.
367 m_current_priority->m_demands_processed = 0;
368
369 // Try to find next subqueue.
370 if( m_current_priority > &m_priorities[ 0 ] )
372 else
373 // Start new iteration from the highest priority.
374 m_current_priority = &m_priorities[
375 to_size_t( so_5::priority_t::p_max ) ];
376 }
377 };
378
379} /* namespace impl */
380
381} /* namespace quoted_round_robin */
382
383} /* namespace prio_one_thread */
384
385} /* namespace disp */
386
387} /* namespace so_5 */
388
389#if defined(__clang__) && (__clang_major__ >= 16)
390#pragma clang diagnostic pop
391#endif
A base class for agents.
Definition agent.hpp:673
An analog of std::lock_guard for MPSC queue lock.
Container for storing parameters for MPSC queue.
An analog of std::unique_lock for MPSC queue lock.
disp_params_t & set_queue_params(queue_traits::queue_params_t p)
Setter for queue parameters.
const queue_traits::queue_params_t & queue_params() const
Getter for queue parameters.
bool operator!() const noexcept
Does this handle contain a reference to dispatcher?
disp_binder_shptr_t binder() const noexcept
Get a binder for that dispatcher.
A demand queue for dispatcher with one common working thread and round-robin processing of prioritise...
void agent_unbound(priority_t priority)
Notification about detachment of an agent from the queue.
demand_queue_t(queue_traits::lock_unique_ptr_t lock, const quotes_t &quotes)
queue_for_one_priority_t m_priorities[static_cast< std::size_t >(priority_t::p_max)+1]
Subqueues for priorities.
void agent_bound(priority_t priority)
Notification about attachment of yet another agent to the queue.
event_queue_t & event_queue_by_priority(priority_t priority)
Get queue for the priority specified.
void cleanup_queue(queue_for_one_priority_t &queue_info)
Destroy all demands in the queue specified.
void push(queue_for_one_priority_t *subqueue, demand_unique_ptr_t demand)
Push a new demand to the queue.
void add_demand_to_queue(queue_for_one_priority_t &queue, demand_unique_ptr_t demand)
Add a new demand to the tail of the queue specified.
disp_data_source_t(const std::string_view name_base, outliving_reference_t< dispatcher_template_t > disp)
void distribute_value_for_priority(const mbox_t &mbox, priority_t priority, std::size_t quote, std::size_t agents_count, std::size_t demands_count)
void distribute(const mbox_t &mbox) override
Send appropriate notification about the current value.
dispatcher_template_t(outliving_reference_t< environment_t > env, const std::string_view name_base, disp_params_t params, const quotes_t &quotes)
void preallocate_resources(agent_t &) override
Allocate resources in dispatcher for new agent.
stats::auto_registered_source_holder_t< disp_data_source_t > m_data_source
Data source for run-time monitoring.
size_t query(priority_t prio) const
Get the quote for a priority.
Definition quotes.hpp:89
quotes_t & set(priority_t prio, std::size_t quote)
Set a new quote for a priority.
Definition quotes.hpp:76
static void ensure_quote_not_zero(std::size_t value)
Definition quotes.hpp:99
Mixin that holds optional work thread factory.
Interface for dispatcher binders.
SObjectizer Environment.
An interface of event queue for agent.
Helper class for indication of long-lived reference via its type.
Definition outliving.hpp:98
A holder for data-souce that should be automatically registered and deregistered in registry.
A type for storing prefix of data_source name.
Definition prefix.hpp:32
An interface of data source.
#define SO_5_FUNC
Definition declspec.hpp:48
#define SO_5_THROW_EXCEPTION(error_code, desc)
Definition exception.hpp:74
Various stuff related to MPSC event queue implementation and tuning.
void send_thread_activity_stats(const so_5::mbox_t &, const stats::prefix_t &, so_5::disp::prio_one_thread::reuse::work_thread_no_activity_tracking_t< demand_queue_t > &)
Implementation details for dispatcher with round-robin policy of handling prioritized events.
Dispatcher which handles events of different priorities in round-robin maner.
dispatcher_handle_t make_dispatcher(environment_t &env, const quotes_t &quotes)
Create an instance of quoted_round_robin dispatcher.
dispatcher_handle_t make_dispatcher(environment_t &env, const std::string_view data_sources_name_base, const quotes_t &quotes)
Create an instance of quoted_round_robin dispatcher.
SO_5_FUNC dispatcher_handle_t make_dispatcher(environment_t &env, const std::string_view data_sources_name_base, const quotes_t &quotes, disp_params_t params)
Create an instance of quoted_round_robin dispatcher.
Reusable code for dispatchers with one working thread for events of all priorities.
Dispatcher with one working thread for events of all priorities.
Reusable components for dispatchers.
Event dispatchers.
Helpers for working with priorities.
Definition priority.hpp:73
All stuff related to run-time monitoring and statistics.
Private part of message limit implementation.
Definition agent.cpp:33
priority_t
Definition of supported priorities.
Definition priority.hpp:28
A description of event execution demand.