Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #84245

A thread-queue

From Bonita Montero <Bonita.Montero@gmail.com>
Newsgroups comp.lang.c++
Subject A thread-queue
Date 2022-05-24 04:07 +0200
Organization A noiseless patient Spider
Message-ID <t6hekd$q6f$1@dont-email.me> (permalink)

Show all headers | View raw


This is a thread-queue I've written:

#pragma once
#include <deque>
#include <mutex>
#include <condition_variable>
#include <utility>
#include <concepts>
#include <list>

template<typename QueueType>
concept thread_queue_concept =
	std::same_as<QueueType, std::deque<typename QueueType::value_type, 
typename QueueType::allocator_type>>
	|| std::same_as<QueueType, std::list<typename QueueType::value_type, 
typename QueueType::allocator_type>>;

template<typename QueueType>
	requires thread_queue_concept<QueueType>
struct thread_queue
{
	using value_type = typename QueueType::value_type;
	thread_queue();
	explicit thread_queue( typename QueueType::allocator_type const &alloc );
	thread_queue( thread_queue &&other );
	thread_queue &operator =( thread_queue const &other );
	thread_queue &operator =( thread_queue &&other );
	bool empty() const;
	std::size_t size() const;
	void shrink_to_fit();
	void clear();
	template<typename ... Args>
		requires std::is_constructible_v<typename QueueType::value_type, Args ...>
	void enque( Args &&... args );
	template<typename Producer>
		requires requires( Producer producer ) { { producer() } -> 
std::same_as<std::pair<bool, typename QueueType::value_type>>; }
	void enqueue_multiple( Producer producer );
	template<typename Consumer>
		requires requires( Consumer consumer, typename QueueType::value_type 
value ) { { consumer( std::move( value ) ) } -> std::same_as<bool>; }
	void dequeue_multiple( Consumer consumer );
	typename QueueType::value_type dequeue();
	void swap( thread_queue &other );
private:
	mutable std::mutex m_mtx;
	mutable std::condition_variable m_cv;
	QueueType m_queue;
};

template<typename QueueType>
	requires thread_queue_concept<QueueType>
thread_queue<QueueType>::thread_queue()
{
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
thread_queue<QueueType>::thread_queue( typename 
QueueType::allocator_type const &alloc ) :
	m_queue( alloc )
{
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
thread_queue<QueueType>::thread_queue( thread_queue &&other )
{
	using namespace std;
	lock_guard lock( other.m_mtx );
	m_queue = move( other.m_queue );
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
thread_queue<QueueType> &thread_queue<QueueType>::thread_queue::operator 
=( thread_queue const &other )
{
	std::lock_guard
		ourLock( m_mtx ),
		otherLock( other.m_mtx );
	m_queue = other.m_queue;
	return *this;
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
thread_queue<QueueType> &thread_queue<QueueType>::thread_queue::operator 
=( thread_queue &&other )
{
	using namespace std;
	lock_guard
		ourLock( m_mtx ),
		otherLock( other.m_mtx );
	m_queue = move( other.m_queue );
	return *this;
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
bool thread_queue<QueueType>::thread_queue::empty() const
{
	std::lock_guard lock( m_mtx );
	return m_queue.empty();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
std::size_t thread_queue<QueueType>::thread_queue::size() const
{
	std::lock_guard lock( m_mtx );
	return m_queue.size();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
void thread_queue<QueueType>::thread_queue::shrink_to_fit()
{
	std::lock_guard lock( m_mtx );
	return m_queue.shrink_to_fit();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
void thread_queue<QueueType>::thread_queue::clear()
{
	std::lock_guard lock( m_mtx );
	m_queue.clear();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
template<typename ... Args>
	requires std::is_constructible_v<typename QueueType::value_type, Args ...>
void thread_queue<QueueType>::thread_queue::enque( Args &&... args )
{
	using namespace std;
	unique_lock lock( m_mtx );
	m_queue.emplace_front( forward<Args>( args ) ... );
	m_cv.notify_one();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
typename QueueType::value_type 
thread_queue<QueueType>::thread_queue::dequeue()
{
	using namespace std;
	unique_lock lock( m_mtx );
	while( m_queue.empty() )
		m_cv.wait( lock );
	value_type value = move( m_queue.back() );
	m_queue.pop_back();
	return value;
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
template<typename Producer>
	requires requires( Producer producer ) { { producer() } -> 
std::same_as<std::pair<bool, typename QueueType::value_type>>; }
void thread_queue<QueueType>::enqueue_multiple( Producer producer )
{
	using namespace std;
	lock_guard lock( m_mtx );
	for( std::pair<bool, value_type> ret; (ret = move( producer() )).first; )
		m_queue.emplace_front( move( ret.second ) ),
		m_cv.notify_one();
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
template<typename Consumer>
	requires requires( Consumer consumer, typename QueueType::value_type 
value ) { { consumer( std::move( value ) ) } -> std::same_as<bool>; }
void thread_queue<QueueType>::dequeue_multiple( Consumer consumer )
{
	using namespace std;
	unique_lock lock( m_mtx );
	for( ; ; )
	{
		while( m_queue.empty() )
			m_cv.wait( lock );
		try
		{
			bool cont = consumer( move( m_queue.back() ) );
			m_queue.pop_back();
			if( !cont )
				return;
		}
		catch( ... )
		{
			m_queue.pop_back();
			throw;
		}
	}
}

template<typename QueueType>
	requires thread_queue_concept<QueueType>
void thread_queue<QueueType>::thread_queue::swap( thread_queue &other )
{
	std::lock_guard
		ourLock( m_mtx ),
		otherLock( other.m_mtx );
	m_queue.swap( other.m_queue );
}


The only template-parameter is BaseType, which can be a std::deque type
or std::list type, restricted with thread_queue_concept. This class uses
this type as the internal queue type. Chose that BaseType that is most
efficient for your application. I might have restricted the class on a
more differentiated thread_queue_concepts that checks for all the used
parts of BaseType so that this class might apply for other types
compatible to std::list<> or std::deque<> but I was too lazy to imple-
ment that for the unlikely case that someone implements something like
that on his own. One advantage of this code are enqueue_multiple and
dequeue_multiple. These functions are given a function-object, usually
a lambda, which can enqueue or dequeue multiple items with only one
locking step. For enqueue this always holds true, for dequeue this
depends on if the queue has elements to fetch or not.
enqueue_multiple usually makes sense if you have one producer and
multiple consumers. It results in longer periods holding the lock and
therefore it makes sense only if the items can be produced or move fast.
dequeue_multiple usually makes sense if you have multiple producers and
one consumer. Here we also have longer locking periods, but as objects
are usually only have fast moves here, this normally doesn't hurt.
If the consumer function object of the dequeue_multiple throws an
exception while consuming, the exception is caugt and the element
provided to the consumer (rvalue-refernce inside the underlying queue
types object) is removed.
If you like to use this class with C++11 you have to remove the concepts
or disable them with #if defined(__cpp_concepts).

Back to comp.lang.c++ | Previous | NextNext in thread | Find similar | Unroll thread


Thread

A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-24 04:07 +0200
  Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-25 13:58 -0700
    Re: A thread-queue Bo Persson <bo@bo-persson.se> - 2022-05-26 01:04 +0200
      Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-25 20:58 -0700
      Re: A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-26 16:20 +0200
        Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-26 22:40 -0700
          Re: A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-27 11:33 +0200
            Re: A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-27 14:26 +0200
              Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-27 14:18 -0700
                Re: A thread-queue scott@slp53.sl.home (Scott Lurndal) - 2022-05-27 21:53 +0000
                Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-27 15:43 -0700
                Re: A thread-queue scott@slp53.sl.home (Scott Lurndal) - 2022-05-28 14:51 +0000
                Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-27 18:24 -0700
                Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-27 18:26 -0700
                Re: A thread-queue scott@slp53.sl.home (Scott Lurndal) - 2022-05-28 14:56 +0000
                Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-28 16:49 -0700
                Re: A thread-queue red floyd <no.spam.here@its.invalid> - 2022-05-28 17:16 -0700
                Re: A thread-queue "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-05-30 16:00 -0700
                Re: A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-28 07:16 +0200
    Re: A thread-queue Bonita Montero <Bonita.Montero@gmail.com> - 2022-05-26 16:15 +0200

csiph-web