lockfree : getting a move only type into spsc_queue
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).
participants (1)
-
Thomas Novotny