|
Boost Users : |
Subject: [Boost-users] lockfree : getting a move only type into spsc_queue
From: Thomas Novotny (tnovotny_at_[hidden])
Date: 2016-07-23 17:36:10
I had a unique_ptr from a pool with a custom deleter that I needed to
get int a lockfree queue. This did not work out of the box with either
boost::lockfree::queue or boost::lockfree::spsc_queue.
There were three main issues:
* default construction
* copying
* copy construction
To solve the issue with default constructability I had to wrap the
unique_ptr in a class.
In the end Imade a custom version of spsc_queue. Getting this working
(as a hack) was fairly easy, so I wanted to share it in the hope that a
better solution might make it into a future version of the library.
In essence, I only had to add some std::move calls and adjust the api
for push.
two lines here:
bool push(T const & t, T * buffer, size_t max_size)
{
const size_t write_index =
write_index_.load(memory_order_relaxed); // only written from push thread
const size_t next = next_index(write_index, max_size);
if (next == read_index_.load(memory_order_acquire))
return false; /* ringbuffer is full */
new (buffer + write_index) T(t); // copy-construct
write_index_.store(next, memory_order_release);
return true;
}
to
bool push( T & t, T * buffer, size_t max_size )
{
const size_t write_index = write_index_.load(
memory_order_relaxed ); // only written from push thread
const size_t next = next_index( write_index, max_size );
if( next == read_index_.load( memory_order_acquire ) )
return false; /* ringbuffer is full */
new (buffer + write_index) T(); // default-construct
*(buffer + write_index) = std::move( t ); // move
write_index_.store( next, memory_order_release );
return true;
}
a line here:
bool push(T const & t)
{
return base_type::push(t);
}
to
bool push(T & t)
{
return base_type::push(t);
}
and a few lines here
template< class OutputIterator >
OutputIterator copy_and_delete( T * first, T * last,
OutputIterator out )
{
if (boost::has_trivial_destructor<T>::value) {
return std::copy(first, last, out); // will use memcpy if
possible
} else {
for (; first != last; ++first, ++out) {
*out = *first;
first->~T();
}
return out;
}
}
to
template< class OutputIterator >
OutputIterator copy_and_delete( T * first, T * last,
OutputIterator out )
{
for (; first != last; ++first, ++out) {
*out = std::move(*first);
first->~T();
}
return out;
}
In copy_payload there is only one change
struct copy_convertible
{
template <typename T, typename U>
static void copy(T & t, U & u)
{
u = t;
}
};
became
template <typename T, typename U>
static void copy(T & t, U & u)
{
u = std::move(t);
}
and that was it. The result is not pretty but works (only single element
push and pop used).
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net