SObjectizer 5.8
Loading...
Searching...
No Matches
queue_of_queues.hpp
Go to the documentation of this file.
1/*
2 * SObjectizer-5
3 */
4
5/*!
6 * \file
7 * \brief Multi-producer/Multi-consumer queue of pointers to event queues.
8 *
9 * \since v.5.4.0, v.5.8.0
10 */
11
12#pragma once
13
14#include <so_5/disp/mpmc_queue_traits/pub.hpp>
15
16#include <deque>
17#include <mutex>
18#include <vector>
19
20namespace so_5
21{
22
23namespace disp
24{
25
26namespace reuse
27{
28
29//
30// queue_of_queues_t
31//
32/*!
33 * \brief Multi-producer/Multi-consumer queue of pointers to event queues.
34 *
35 * \note
36 * Since v.5.8.0 this type implements intrusive queue and requires that
37 * type \a T provides the following methods:
38 * \code
39 * T * intrusive_queue_giveout_next() noexcept;
40 * void intrusive_queue_set_next( T * next ) noexcept;
41 * \endcode
42 *
43 * \tparam T type of event queue.
44 *
45 * \since v.5.4.0, v.5.8.0
46 */
47template< class T >
49 {
50 public :
51 using item_t = T;
52
54 const so_5::disp::mpmc_queue_traits::queue_params_t & queue_params,
55 std::size_t thread_count )
56 : m_lock{ queue_params.lock_factory()() }
57 , m_max_thread_count{ thread_count }
60 {
61 // Reserve some space for storing infos about waiting
62 // customer threads.
63 m_waiting_customers.reserve( thread_count );
64 }
65
66 //! Initiate shutdown for working threads.
67 inline void
68 shutdown() noexcept
69 {
70 std::lock_guard< so_5::disp::mpmc_queue_traits::lock_t > lock{ *m_lock };
71
72 m_shutdown = true;
73
74 while( !m_waiting_customers.empty() )
76 }
77
78 //! Get next active queue.
79 /*!
80 * \retval nullptr is the case of dispatcher shutdown.
81 */
82 inline T *
83 pop( so_5::disp::mpmc_queue_traits::condition_t & condition ) noexcept
84 {
85 std::lock_guard< so_5::disp::mpmc_queue_traits::lock_t > lock{ *m_lock };
86
87 do
88 {
89 if( m_shutdown )
90 break;
91
92 if( m_head )
93 {
94 // The queue isn't empty, the head has to be extracted.
95 auto r = pop_head();
96
97 // There could be non-empty queue and sleeping workers...
99
100 return r;
101 }
102
103 // Exception safety note: it seems that there should not be
104 // dynamic memory allocation because m_waiting_customers is
105 // reserved in the constructor and only push_back and pop_front
106 // are used. So if the actual count of worker threads equals
107 // to thread_count constructor's parameter, then there is
108 // no need to expand m_waiting_customers vector.
109 m_waiting_customers.push_back( &condition );
110
111 condition.wait();
112 // If we are here then the current wakeup procedure is
113 // finished.
114 m_wakeup_in_progress = false;
115 }
116 while( true );
117
118 return nullptr;
119 }
120
121 //! Switch the current non-empty queue to another one if it is possible.
122 /*!
123 * \return nullptr is the case of dispatcher shutdown.
124 *
125 * \since v.5.5.15.1
126 */
127 [[nodiscard]]
128 inline T *
129 try_switch_to_another( T * current ) noexcept
130 {
132
133 if( m_shutdown )
134 return nullptr;
135
136 if( m_head )
137 {
138 auto r = pop_head();
139
140 // Old non-empty queue must be stored for further processing.
141 // No need to wakup someone because the length of the queue
142 // didn't changed.
144
145 return r;
146 }
147
148 return current;
149 }
150
151 //! Schedule execution of demands from the queue.
152 void
153 schedule( T * queue ) noexcept
154 {
155 std::lock_guard< so_5::disp::mpmc_queue_traits::lock_t > lock{ *m_lock };
156
157 push_to_queue( queue );
158
160 }
161
162 so_5::disp::mpmc_queue_traits::condition_unique_ptr_t
167
168 private :
169 //! Object's lock.
170 so_5::disp::mpmc_queue_traits::lock_unique_ptr_t m_lock;
171
172 //! Shutdown flag.
173 bool m_shutdown{ false };
174
175 /*!
176 * \brief The current head of the intrusive queue.
177 *
178 * Holds nullptr if the queue is empty.
179 *
180 * \since v.5.8.0
181 */
182 T * m_head{ nullptr };
183
184 /*!
185 * \brief The current tail of the intrusive queue.
186 *
187 * Holds nullptr if the queue is empty.
188 * It is equal to m_head if the queue contains just one item.
189 *
190 * \since v.5.8.0
191 */
192 T * m_tail{ nullptr };
193
194 /*!
195 * \brief The current size of the intrusive queue.
196 *
197 * \since v.5.8.0
198 */
199 std::size_t m_queue_size{};
200
201 /*!
202 * \brief Is some working thread in wakeup process now?
203 *
204 * \since v.5.5.15.1
205 *
206 */
207 bool m_wakeup_in_progress{ false };
208
209 /*!
210 * \brief Maximum count of working threads to be used with
211 * that mpmc_queue.
212 *
213 * \since v.5.5.16
214 */
215 const std::size_t m_max_thread_count;
216
217 /*!
218 * \brief Threshold for wake up next working thread if there are
219 * non-empty agent queues.
220 *
221 * \since v.5.5.16
222 */
224
225 //! Waiting threads.
227
228 void
230 {
231 auto & condition = *m_waiting_customers.back();
232 m_waiting_customers.pop_back();
233
235 condition.notify();
236 }
237
238 /*!
239 * \brief An attempt to wakeup another sleeping thread if it's necessary
240 * and possible.
241 *
242 * \note Since v.5.5.16 there are some changes in wakeup conditions.
243 * A working thread is awakened when:
244 * - there are something in the queue;
245 * - there are waiting customers but no one of them is in wakeup now;
246 * - count of items in the queue is greater than
247 * m_next_thread_wakeup_threshold or there is no active customers at
248 * all.
249 *
250 * \since v.5.5.15.1
251 */
252 void
264
265 /*!
266 * \brief Helper method that extracts the head item from the queue.
267 *
268 * \attention
269 * This method must only be called if the queue isn't empty.
270 * The method doesn't check this condition by itself.
271 *
272 * \since v.5.8.0
273 */
274 [[nodiscard]]
275 T *
276 pop_head() noexcept
277 {
278 auto r = m_head;
279 m_head = r->intrusive_queue_giveout_next();
280 if( !m_head )
281 m_tail = nullptr;
282 --m_queue_size;
283
284 return r;
285 }
286
287 /*!
288 * \brief Helper method that pushes a new item to the end of the queue.
289 *
290 * \since v.5.8.0
291 */
292 void
293 push_to_queue( T * new_tail ) noexcept
294 {
295 if( m_tail )
296 {
297 m_tail->intrusive_queue_set_next( new_tail );
298 m_tail = new_tail;
299 }
300 else
301 {
302 m_head = m_tail = new_tail;
303 }
304 ++m_queue_size;
305 }
306 };
307
308} /* namespace reuse */
309
310} /* namespace disp */
311
312} /* namespace so_5 */
#define SO_5_CHECK_INVARIANT(what, data)
A base class for agents.
Definition agent.hpp:673
static execution_hint_t so_create_execution_hint(execution_demand_t &demand)
Create execution hint for the specified demand.
Definition agent.cpp:901
void so_bind_to_dispatcher(event_queue_t &queue) noexcept
Binding agent to the dispatcher.
Definition agent.cpp:872
The base class for the object with a reference counting.
Parameters for binding agents to adv_thread_pool dispatcher.
bind_params_t & fifo(fifo_t v)
Set FIFO type.
Alias for namespace with traits of event queue.
std::size_t thread_count() const
Getter for thread count.
disp_params_t & thread_count(std::size_t count)
Setter for thread count.
disp_params_t & tune_queue_params(L tunner)
Tuner for queue parameters.
std::size_t m_thread_count
Count of working threads.
queue_traits::queue_params_t m_queue_params
Queue parameters.
const queue_traits::queue_params_t & queue_params() const
Getter for queue parameters.
disp_params_t & set_queue_params(queue_traits::queue_params_t p)
Setter for queue parameters.
friend void swap(disp_params_t &a, disp_params_t &b) noexcept
A handle for adv_thread_pool dispatcher.
disp_binder_shptr_t binder(bind_params_t params) const
Get a binder for that dispatcher.
std::enable_if_t< std::is_invocable_v< Setter, bind_params_t & >, disp_binder_shptr_t > binder(Setter &&params_setter) const
Create a binder for that dispatcher.
disp_binder_shptr_t binder() const
Get a binder for that dispatcher with default binding params.
bool operator!() const noexcept
Does this handle contain a reference to dispatcher?
bool empty() const noexcept
Is this handle empty?
impl::basic_dispatcher_iface_shptr_t m_dispatcher
A reference to actual implementation of a dispatcher.
dispatcher_handle_t(impl::basic_dispatcher_iface_shptr_t dispatcher) noexcept
void reset() noexcept
Drop the content of handle.
operator bool() const noexcept
Is this handle empty?
void bind(agent_t &agent) noexcept override
Bind agent to dispatcher.
const bind_params_t m_params
Binding parameters.
void unbind(agent_t &agent) noexcept override
Unbind agent from dispatcher.
actual_binder_t(actual_dispatcher_iface_shptr_t disp, bind_params_t params) noexcept
actual_dispatcher_iface_shptr_t m_disp
Dispatcher to be used.
void preallocate_resources(agent_t &agent) override
Allocate resources in dispatcher for new agent.
void undo_preallocation(agent_t &agent) noexcept override
Undo resources allocation.
virtual event_queue_t * query_resources_for_agent(agent_t &agent) noexcept=0
Get resources allocated for an agent.
virtual void undo_preallocation_for_agent(agent_t &agent) noexcept=0
Undo preallocation of resources for a new agent.
virtual void unbind_agent(agent_t &agent) noexcept=0
Unbind agent from the dispatcher.
virtual void preallocate_resources_for_agent(agent_t &agent, const bind_params_t &params)=0
Preallocate all necessary resources for a new agent.
event_queue_t * query_resources_for_agent(agent_t &agent) noexcept override
Get resources allocated for an agent.
void unbind_agent(agent_t &agent) noexcept override
Unbind agent from the dispatcher.
void undo_preallocation_for_agent(agent_t &agent) noexcept override
Undo preallocation of resources for a new agent.
actual_dispatcher_implementation_t(outliving_reference_t< environment_t > env, const std::string_view name_base, disp_params_t params)
dispatcher_template_t< Work_Thread > m_impl
Real dispatcher.
void preallocate_resources_for_agent(agent_t &agent, const bind_params_t &params) override
Preallocate all necessary resources for a new agent.
execution_demand_t peek_front()
Get the information about the front demand.
agent_queue_t * intrusive_queue_giveout_next() noexcept
Give away a pointer to the next agent_queue.
void push(execution_demand_t demand) override
Push next demand to queue.
std::size_t size() const noexcept
Get the current size of the queue.
agent_queue_t * m_intrusive_queue_next
The next item in intrusive queue of agent_queues.
void intrusive_queue_set_next(agent_queue_t *next) noexcept
Set a pointer to the next agent_queue.
agent_queue_t(outliving_reference_t< dispatcher_queue_t > disp_queue, const bind_params_t &)
Constructor.
bool worker_finished(unsigned int type_of_worker)
Signal about finishing of worker of the specified type.
void push_evt_finish(execution_demand_t demand) noexcept override
std::atomic< std::size_t > m_size
Current size of the queue.
bool is_there_not_thread_safe_worker() const
Check the presence of thread unsafe worker.
void delete_head() noexcept
Helper method for deleting queue's head object.
bool worker_started(unsigned int type_of_worker)
Remove the front demand.
spinlock_t & lock() noexcept
Access to the queue's lock.
void push_evt_start(execution_demand_t demand) override
bool is_there_any_worker() const
Check the presence of any worker at the moment.
The very basic interface of adv_thread_pool dispatcher.
virtual disp_binder_shptr_t binder(bind_params_t params)=0
static dispatcher_handle_t make(actual_dispatcher_iface_shptr_t disp) noexcept
no_activity_tracking_impl_t(outliving_reference_t< dispatcher_queue_t > queue, work_thread_holder_t thread_holder)
Initializing constructor.
so_5::stats::activity_tracking_stuff::stats_collector_t< so_5::stats::activity_tracking_stuff::external_lock<> > m_waiting_stats_collector
A collector for waiting stats.
with_activity_tracking_impl_t(outliving_reference_t< dispatcher_queue_t > queue, work_thread_holder_t thread_holder)
Initializing constructor.
so_5::stats::activity_tracking_stuff::stats_collector_t< so_5::stats::activity_tracking_stuff::external_lock<> > m_work_activity_collector
A collector for work activity.
void process_queue(agent_queue_t &queue)
Processing of demands from agent queue.
work_thread_template_t(outliving_reference_t< dispatcher_queue_t > queue, work_thread_holder_t thread_holder)
Initializing constructor.
agent_queue_t * pop_agent_queue() noexcept
An attempt of extraction of non-empty agent queue.
An interface for somethine like condition variable for waiting on MPMC queue lock.
virtual void notify() noexcept=0
Notification for waiting customer.
virtual void wait() noexcept=0
Waiting on condition.
An interface for lock for MPMC queue.
virtual condition_unique_ptr_t allocate_condition()=0
Create condition object for another MPMC queue's customer.
Container for storing parameters for MPMC queue.
queue_params_t & operator=(queue_params_t &&o) noexcept
Move operator.
friend void swap(queue_params_t &a, queue_params_t &b) noexcept
std::size_t next_thread_wakeup_threshold() const
Getter for thread wakeup threshold value.
const lock_factory_t & lock_factory() const
Getter for lock factory.
Multi-producer/Multi-consumer queue of pointers to event queues.
T * m_tail
The current tail of the intrusive queue.
T * try_switch_to_another(T *current) noexcept
Switch the current non-empty queue to another one if it is possible.
void try_wakeup_someone_if_possible() noexcept
An attempt to wakeup another sleeping thread if it's necessary and possible.
const std::size_t m_max_thread_count
Maximum count of working threads to be used with that mpmc_queue.
so_5::disp::mpmc_queue_traits::lock_unique_ptr_t m_lock
Object's lock.
std::size_t m_queue_size
The current size of the intrusive queue.
void schedule(T *queue) noexcept
Schedule execution of demands from the queue.
T * m_head
The current head of the intrusive queue.
queue_of_queues_t(const so_5::disp::mpmc_queue_traits::queue_params_t &queue_params, std::size_t thread_count)
void shutdown() noexcept
Initiate shutdown for working threads.
const std::size_t m_next_thread_wakeup_threshold
Threshold for wake up next working thread if there are non-empty agent queues.
so_5::disp::mpmc_queue_traits::condition_unique_ptr_t allocate_condition()
T * pop(so_5::disp::mpmc_queue_traits::condition_t &condition) noexcept
Get next active queue.
void push_to_queue(T *new_tail) noexcept
Helper method that pushes a new item to the end of the queue.
bool m_wakeup_in_progress
Is some working thread in wakeup process now?
T * pop_head() noexcept
Helper method that extracts the head item from the queue.
std::vector< so_5::disp::mpmc_queue_traits::condition_t * > m_waiting_customers
Waiting threads.
friend void swap(work_thread_activity_tracking_flag_mixin_t &a, work_thread_activity_tracking_flag_mixin_t &b) noexcept
Mixin that holds optional work thread factory.
friend void swap(work_thread_factory_mixin_t &a, work_thread_factory_mixin_t &b) noexcept
An analog of unique_ptr for abstract_work_thread.
work_thread_holder_t(work_thread_holder_t &&o) noexcept
Interface for dispatcher binders.
SObjectizer Environment.
An interface of event queue for agent.
bool is_thread_safe() const
Is thread safe handler?
void exec(current_thread_id_t working_thread_id) const
Call event handler directly.
Template class for smart reference wrapper on the atomic_refcounted_t.
intrusive_ptr_t(T *obj) noexcept
Constructor for a raw pointer.
Helper class for indication of long-lived reference via its type.
Definition outliving.hpp:98
T & get() const noexcept
Base for the case of externals stats lock.
#define SO_5_FUNC
Definition declspec.hpp:48
void adjust_thread_count(disp_params_t &params)
Sets the thread count to default value if used do not specify actual thread count.
Internal implementation details of advanced thread pool dispatcher.
Advanced thread pool dispatcher.
dispatcher_handle_t make_dispatcher(environment_t &env, const std::string_view data_sources_name_base, std::size_t thread_count)
Create an instance of adv_thread_pool dispatcher.
dispatcher_handle_t make_dispatcher(environment_t &env, std::size_t thread_count)
Create an instance of adv_thread_pool dispatcher.
dispatcher_handle_t make_dispatcher(environment_t &env)
Create an instance of adv_thread_pool dispatcher with the default count of work threads.
SO_5_FUNC dispatcher_handle_t make_dispatcher(environment_t &env, const std::string_view data_sources_name_base, disp_params_t params)
Create an instance of adv_thread_pool dispatcher.
fifo_t
Type of FIFO mechanism for agent's demands.
@ cooperation
A FIFO for demands for all agents from the same cooperation.
@ individual
A FIFO for demands only for one agent.
Various stuff related to MPMC event queue implementation and tuning.
Helper tools for implementation of run-time monitoring for thread-pool-like dispatchers.
Reusable components for dispatchers.
std::size_t default_thread_pool_size()
A helper function for detecting default thread count for thread pool.
std::unique_ptr< Disp_Iface_Type > make_actual_dispatcher(outliving_reference_t< environment_t > env, const std::string_view name_base, Disp_Params_Type disp_params, Args &&...args)
Helper function for creation of dispatcher instance with respect to work thread activity tracking fla...
Reusable implementation of some thread pool dispatcher functionality which can be used in other threa...
Thread pool dispatcher.
Event dispatchers.
Details of SObjectizer run-time implementations.
Definition agent.cpp:780
void ensure_join_from_different_thread(current_thread_id_t thread_to_be_joined)
Ensures that join will be called from different thread.
All stuff related to run-time monitoring and statistics.
Private part of message limit implementation.
Definition agent.cpp:33
current_thread_id_t query_current_thread_id()
Get the ID of the current thread.
outliving_reference_t< T > outliving_mutable(T &r)
Make outliving_reference wrapper for mutable reference.
Adaptation of common implementation of thread-pool-like dispatcher to the specific of this thread-poo...
static bool is_individual_fifo(const bind_params_t &params) noexcept
static constexpr std::string_view dispatcher_type_name() noexcept
static void wait_for_queue_emptyness(agent_queue_t &) noexcept
common_data_t(outliving_reference_t< dispatcher_queue_t > queue, work_thread_holder_t thread_holder)
so_5::disp::mpmc_queue_traits::condition_unique_ptr_t m_condition
Waiting object for long wait.
A description of event execution demand.
agent_t * m_receiver
Receiver of demand.
Various traits of activity tracking implementation.
activity_stats_t m_working_stats
Stats for processed events.
activity_stats_t m_waiting_stats
Stats for waiting periods.