/* ATTENTION: This header is part of the PCH Remove the include in all StdAfx.h files or live with the consequences :) */ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H #include #include #include #include namespace Pol { namespace Clib { template class message_queue { public: message_queue(); ~message_queue(); message_queue( const message_queue& ) = delete; message_queue& operator=( const message_queue& ) = delete; // push new message into queue and notify possible wait_pop void push( Message const& msg ); void push( std::list& msg_list ); // push new message into queue and notify possible wait_pop // will move the msg into the queue, thus the reference is likely to be // invalid afterwards void push_move( Message&& msg ); // check if empty (a bit senseless) bool empty() const; // return current size (unsafe aka senseless) std::size_t size() const; // tries to get a message true on success false otherwise bool try_pop( Message* msg ); // waits till queue is non empty void pop_wait( Message* msg ); // waits till queue is non empty and fill list void pop_wait( std::list* msgs ); // empties the queue (unsafe) void pop_remaining( std::list* msgs ); void cancel(); struct Canceled { }; private: std::list _queue; mutable std::mutex _mutex; std::condition_variable _notifier; bool _cancel; }; template message_queue::message_queue() : _queue(), _mutex(), _notifier(), _cancel( false ) { } template message_queue::~message_queue() { cancel(); } template void message_queue::push( Message const& msg ) { std::list tmp; tmp.push_back( msg ); // costly pushback outside the lock bool signal = false; { std::lock_guard lock( _mutex ); signal = _queue.empty(); _queue.splice( _queue.end(), tmp ); // fast splice inside } if ( signal ) _notifier.notify_one(); } template void message_queue::push_move( Message&& msg ) { std::list tmp; tmp.emplace_back( std::move( msg ) ); // costly pushback outside the lock bool signal = false; { std::lock_guard lock( _mutex ); signal = _queue.empty(); _queue.splice( _queue.end(), tmp ); // fast splice inside } if ( signal ) _notifier.notify_one(); } template void message_queue::push( std::list& msg_list ) { bool signal = false; { std::lock_guard lock( _mutex ); signal = _queue.empty(); _queue.splice( _queue.end(), msg_list ); // fast splice inside } if ( signal ) _notifier.notify_one(); } template bool message_queue::empty() const { std::lock_guard lock( _mutex ); return _queue.empty(); } template std::size_t message_queue::size() const { std::lock_guard lock( _mutex ); return _queue.size(); } /// tries to get a message true on success false otherwise template bool message_queue::try_pop( Message* msg ) { std::lock_guard lock( _mutex ); if ( _queue.empty() ) return false; *msg = std::move( _queue.front() ); _queue.pop_front(); return true; } template void message_queue::pop_wait( Message* msg ) { std::unique_lock lock( _mutex ); while ( _queue.empty() && !_cancel ) _notifier.wait( lock ); // will unlock mutex during wait if ( _cancel ) throw Canceled(); *msg = std::move( _queue.front() ); _queue.pop_front(); } template void message_queue::pop_wait( std::list* msgs ) { std::unique_lock lock( _mutex ); while ( _queue.empty() && !_cancel ) _notifier.wait( lock ); // will unlock mutex during wait if ( _cancel ) throw Canceled(); msgs->splice( msgs->end(), _queue ); } template void message_queue::pop_remaining( std::list* msgs ) { std::unique_lock lock( _mutex ); msgs->splice( msgs->end(), _queue ); } template void message_queue::cancel() { { std::lock_guard lock( _mutex ); _cancel = true; } _notifier.notify_all(); } } // namespace Clib } // namespace Pol #endif