diff --git a/cds/container/williams_queue.h b/cds/container/williams_queue.h new file mode 100644 index 000000000..03f23ced1 --- /dev/null +++ b/cds/container/williams_queue.h @@ -0,0 +1,493 @@ +//$$CDS-header$$ + +#ifndef CDSLIB_CONTAINER_WILLIAMS_QUEUE_H +#define CDSLIB_CONTAINER_WILLIAMS_QUEUE_H + +#include +#include + +#ifdef UNICODE +#define tstring std::wstring +#define tostringstream std::wostringstream +#else +#define tstring std::string +#define tostringstream std::ostringstream +#endif +#define FMTDBGSTR(stream) ((tostringstream&)(tostringstream() << tstring() << stream)).str().c_str() + +namespace cds { + namespace container { + /// Anthony Williams' queue + /** @ingroup cds_nonintrusive_helper + */ + namespace williams_queue { + /// WilliamsQueue default type traits + struct traits + { + /// Node allocator + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting + typedef atomicity::empty_item_counter item_counter; + }; + + /// Metafunction converting option list to \p williams_queue::traits + /** + Supported \p Options are: + - \p opt::allocator - allocator (like \p std::allocator) used for allocating queue nodes. Default is \ref CDS_DEFAULT_ALLOCATOR + - \p opt::item_counter - the type of item counting feature. Default is \p cds::atomicity::empty_item_counter (item counting disabled) + To enable item counting use \p cds::atomicity::item_counter + + Example: declare \p %WilliamsQueue with item counting + \code + typedef cds::container::WilliamsQueue< Foo, + typename cds::container::williams_queue::make_traits< + cds::opt::item_counter< cds::atomicity::item_counter > > + >::type + > williamsQueue; + \endcode + */ + template + struct make_traits { +#ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type; ///< Metafunction result +#else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< traits, Options... >::type + , Options... + >::type type; +#endif + }; + } // namespace williams_queue + + /// Williams lock-free queue + /** @ingroup cds_nonintrusive_queue + The queue uses in-built internal and external reference counting for each node. + The queue does not require any garbage collector. + + Source + - [2012] Anthony Williams "C++ Concurrency in Action" + + Template arguments: + - \p T is a type stored in the queue. + - \p Traits - queue traits, default is \p williams_queue::traits. You can use \p williams_queue::make_traits + metafunction to make your traits or just derive your traits from \p %williams_queue::traits: + \code + struct myTraits: public cds::container::williams_queue::traits { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::WilliamsQueue< Foo, myTraits > myQueue; + + // Equivalent make_traits example: + typedef cds::container::WilliamsQueue< Foo, + typename cds::container::williams_queue::make_traits< + cds::opt::item_counter< cds::atomicity::item_counter > + >::type + > myQueue; + \endcode + */ + template + class WilliamsQueue + { + public: + /// Rebing template arguments + template + struct rebind { + typedef WilliamsQueue< T2, Traits2 > other; ///< Rebinding result + }; + + typedef T value_type; ///< The value type to be stored in the queue + typedef Traits traits; ///< Queue traits + + typedef typename traits::item_counter item_counter; ///< Item counting policy used + + protected: + //@cond + struct node_type; + /// Wraps external count together with the node_type pointer + struct counted_node_ptr + { + int external_count; + node_type * ptr; + }; + + /** + Note that we need only 2 bits for the external_counters + because there are at most two such counters. + By using a bit field for this and specifying + internal_count as a 30-bit value, we keep + the total counter size to 32 bits. This gives + us plenty of scope for large internal count values + while ensuring that the whole structure fits + inside a machine word on 32-bit and 64-bit machines. + It’s important to update these counts together as a + single entity in order to avoid race conditions + */ + struct node_counter + { + unsigned internal_count : 30; + unsigned external_counters : 2; + }; + + /// Node type + struct node_type + { + atomics::atomic< value_type* > m_value; ///< Value stored in the node + atomics::atomic< node_counter > m_count; ///< Internal and external reference counter + atomics::atomic< counted_node_ptr > m_next; ///< Pointer to the next node in the queue + + node_type() : m_value(nullptr) + { + node_counter new_count; + new_count.internal_count = 0; + new_count.external_counters = 2; + m_count.store(new_count); + + counted_node_ptr new_next = { 0 }; + m_next.store(new_next); + } + }; + + atomics::atomic< counted_node_ptr > m_Head; + atomics::atomic< counted_node_ptr > m_Tail; + + item_counter m_ItemCounter; + //@endcond + + public: + typedef typename traits::allocator::template rebind< node_type >::other allocator_type; ///< Allocator type used for allocate/deallocate the queue nodes + + protected: + //@cond + typedef cds::details::Allocator< node_type, allocator_type > node_allocator; + typedef std::unique_ptr< value_type > scoped_value_ptr; + + static node_type * alloc_node() + { + return node_allocator().New(); + } + static node_type * alloc_node(value_type * const val) + { + node_type * new_node = alloc_node(); + new_node->m_value.store(val, atomics::memory_order_relaxed); + return new_node; + } + static node_type * alloc_node(value_type const& val) + { + scoped_value_ptr new_value(new value_type(val)); + node_type * new_node = alloc_node(new_value.get()); + new_value.release(); + return new_node; + } + template + static node_type * alloc_node_move(Args&&... args) + { + scoped_value_ptr new_value(new value_type(std::forward(args)...)); + node_type * new_node = alloc_node(new_value.get()); + new_value.release(); + return new_node; + } + static void free_node(node_type * p) + { + node_allocator().Delete(p); + } + static void release_ref(node_type * p) + { + node_counter old_counter = p->m_count.load(atomics::memory_order_relaxed); + node_counter new_counter; + + do + { + new_counter = old_counter; + --new_counter.internal_count; + } while (!p->m_count.compare_exchange_strong( + old_counter, new_counter, + atomics::memory_order_acquire, atomics::memory_order_relaxed)); + + if (!new_counter.internal_count && !new_counter.external_counters) { + p->m_value.exchange(nullptr); + free_node(p); + } + } + static void increase_external_count(atomics::atomic& counter, counted_node_ptr& old_counter) + { + counted_node_ptr new_counter; + + do + { + new_counter = old_counter; + ++new_counter.external_count; + } while (!counter.compare_exchange_strong( + old_counter, new_counter, + atomics::memory_order_acquire, atomics::memory_order_relaxed)); + old_counter.external_count = new_counter.external_count; + } + static void free_external_counter(counted_node_ptr& old_node_ptr) + { + node_type * const ptr = old_node_ptr.ptr; + int const count_increase = old_node_ptr.external_count - 2; + + node_counter old_counter = ptr->m_count.load(atomics::memory_order_relaxed); + node_counter new_counter; + + do + { + new_counter = old_counter; + --new_counter.external_counters; + new_counter.internal_count += count_increase; + } while (!ptr->m_count.compare_exchange_strong( + old_counter, new_counter, + atomics::memory_order_acquire, atomics::memory_order_relaxed)); + + if (!new_counter.internal_count && !new_counter.external_counters) { + free_node(ptr); + } + } + void set_new_tail(counted_node_ptr& old_tail, counted_node_ptr const& new_tail) + { + node_type * const current_tail_ptr = old_tail.ptr; + while (!m_Tail.compare_exchange_weak(old_tail, new_tail) + && old_tail.ptr == current_tail_ptr); + if (old_tail.ptr == current_tail_ptr) + free_external_counter(old_tail); + else + release_ref(current_tail_ptr); + } + bool enqueue_value_ptr(scoped_value_ptr& value_ptr) + { + counted_node_ptr new_next; + new_next.ptr = alloc_node(); + new_next.external_count = 1; + + counted_node_ptr old_tail = m_Tail.load(); + + while (true) + { + increase_external_count(m_Tail, old_tail); + value_type * old_value = nullptr; + if (old_tail.ptr->m_value.compare_exchange_strong(old_value, value_ptr.get())) + { + + counted_node_ptr old_next = { 0 }; + if (!old_tail.ptr->m_next.compare_exchange_strong(old_next, new_next)) + { + free_node(new_next.ptr); + new_next = old_next; + } + set_new_tail(old_tail, new_next); + value_ptr.release(); + break; + } + else + { + counted_node_ptr old_next = { 0 }; + if (old_tail.ptr->m_next.compare_exchange_strong(old_next, new_next)) + { + old_next = new_next; + new_next.ptr = alloc_node(); + } + set_new_tail(old_tail, old_next); + } + } + + ++m_ItemCounter; + return true; + } + struct node_disposer + { + void operator()(node_type * p) + { + free_node(p); + } + }; + //@endcond + + public: + /// Initializes empty queue + WilliamsQueue() + { + counted_node_ptr dummy_node; + dummy_node.ptr = alloc_node(); + dummy_node.external_count = 1; + + m_Head.store(dummy_node); + m_Tail.store(dummy_node); + } + + /// Destructor clears the queue + ~WilliamsQueue() + { + clear(); + assert(m_Head.load().ptr == m_Tail.load().ptr); + free_node(m_Head.load().ptr); + } + + //@cond + WilliamsQueue(const WilliamsQueue& other) = delete; + WilliamsQueue& operator=(const WilliamsQueue& other) = delete; + //@endcond + + /// Enqueues \p val value into the queue. Always returns \a true + bool enqueue(value_type const& val) + { + scoped_value_ptr new_value_ptr(new value_type(val)); + return enqueue_value_ptr(new_value_ptr); + } + + /// Enqueues \p data, move semantics + bool enqueue(value_type&& data) + { + scoped_value_ptr new_value_ptr(new value_type(std::forward(data))); + return enqueue_value_ptr(new_value_ptr); + } + + /// Enqueues data to the queue using a functor + /** + \p Func is a functor called to create node. + The functor \p f takes one argument - a reference to a new node of type \ref value_type : + \code + cds::container::WilliamsQueue< Foo > myQueue; + Bar bar; + myQueue.enqueue_with( [&bar]( Foo& dest ) { dest = bar; } ); + \endcode + */ + template + bool enqueue_with(Func f) + { + value_type val; + f(val); + return enqueue(val); + } + + /// Enqueues data of type \ref value_type constructed from std::forward(args)... + template + bool emplace(Args&&... args) + { + value_type val(std::forward(args)...); + return enqueue(val); + } + + /// Synonym for \p enqueue() function + bool push(value_type const& val) + { + return enqueue(val); + } + + /// Synonym for \p enqueue( value_type&& ) function + bool push(value_type&& val) + { + return enqueue(std::move(val)); + } + + /// Synonym for \p enqueue_with() function + template + bool push_with(Func f) + { + return enqueue_with(f); + } + + /// Dequeues a value from the queue + /** + If queue is not empty, the function returns \p true, \p dest contains copy of + dequeued value. The assignment operator for type \ref value_type is invoked. + If queue is empty, the function returns \p false, \p dest is unchanged. + */ + bool dequeue(value_type& dest) + { + return dequeue_with([&dest](value_type& src) { dest = src; }); + } + + /// Dequeues a value using a functor + /** + \p Func is a functor called to copy dequeued value. + The functor takes one argument - a reference to removed node: + \code + cds:container::WilliamsQueue< Foo > myQueue; + Bar bar; + myQueue.dequeue_with( [&bar]( Foo& src ) { bar = std::move( src );}); + \endcode + The functor is called only if the queue is not empty. + */ + template + bool dequeue_with(Func f) + { + counted_node_ptr old_head = m_Head.load(atomics::memory_order_relaxed); + + while (true) + { + increase_external_count(m_Head, old_head); + node_type * const p = old_head.ptr; + if (p == m_Tail.load().ptr) + { + release_ref(p); + return false; + } + counted_node_ptr next = p->m_next.load(); + if (m_Head.compare_exchange_strong(old_head, next)) + { + scoped_value_ptr res(p->m_value.load()); + free_external_counter(old_head); + --m_ItemCounter; + f(*res.get()); + + return true; + } + release_ref(p); + } + } + + /// Synonym for \p dequeue() function + bool pop(value_type& dest) + { + return dequeue(dest); + } + + /// Synonym for \p dequeue_with() function + template + bool pop_with(Func f) + { + return dequeue_with(f); + } + + /// Clear the queue + /** + The function repeatedly calls \ref dequeue until it returns \a false. + */ + void clear() + { + value_type v; + while (dequeue(v)); + } + + /// Checks whether the queue is empty + bool empty() const + { + return m_Head.load().ptr == m_Tail.load().ptr; + } + + /// Returns queue's item count + /** + The value returned depends on \p williams_queue::traits::item_counter. + For \p atomicity::empty_item_counter, this function always returns 0. + + @note Even if you use real item counter and it returns 0, this fact is not + mean that the queue is empty. To check queue emptyness use \p empty() instead. + */ + size_t size() const + { + return m_ItemCounter.value(); + } + + //@cond + /// The class has no internal statistics. For test consistency only + std::nullptr_t statistics() const + { + return nullptr; + } + //@endcond + }; + + } +} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_WILLIAMS_QUEUE_H diff --git a/cds/container/williams_queue_spsc.h b/cds/container/williams_queue_spsc.h new file mode 100644 index 000000000..67b1e640e --- /dev/null +++ b/cds/container/williams_queue_spsc.h @@ -0,0 +1,203 @@ +#ifndef CDSLIB_CONTAINER_WilliamsQueueSPSC_H +#define CDSLIB_CONTAINER_WilliamsQueueSPSC_H + +#include +#include + +namespace cds +{ + namespace container + { + namespace williams_queue_spsc + { + struct traits + { + /// Node allocator + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting + typedef atomicity::empty_item_counter item_counter; + }; + + template + struct make_traits { +#ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type; ///< Metafunction result +#else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< traits, Options... >::type + , Options... + >::type type; +#endif + }; + } + + template + class WilliamsQueueSPSC + { + public: + typedef T value_type; + typedef typename traits::item_counter item_counter; + private: + struct node + { + std::shared_ptr data; + node *next; + + node() : next(nullptr) {} + }; + + item_counter itemCounter; + + std::atomic head; + std::atomic tail; + + + + bool enqueue(std::shared_ptr value) + { + node *p = new node; + node *const old_tail = tail.load(); + old_tail->data.swap(value); + old_tail->next = p; + tail.store(p); + itemCounter++; + return true; + } + + node *dequeue_head() + { + node *const old_head = head.load(); + if (old_head == tail.load()) + { + return nullptr; + } + head.store(old_head->next); + itemCounter--; + return old_head; + } + public: + + WilliamsQueueSPSC() : head(new node), tail(head.load()) {} + + WilliamsQueueSPSC(const WilliamsQueueSPSC &other) = delete; + WilliamsQueueSPSC &operator=(const WilliamsQueueSPSC &other) = delete; + + ~WilliamsQueueSPSC() + { + while (node *const old_head = head.load()) + { + head.store(old_head->next); + delete old_head; + } + } + + + + + bool enqueue(value_type const& value) + { + std::shared_ptr new_data(std::make_shared(value)); + return enqueue(new_data); + } + + + bool enqueue(value_type&& value) + { + std::shared_ptr new_data(std::make_shared(value)); + return enqueue(new_data); + } + + + bool push(value_type const& value) + { + return enqueue(value); + } + + bool push(value_type&& value) + { + return enqueue(value); + } + + template + bool enqueue_with(Func f) + { + value_type val; + f(val); + return enqueue(val); + } + + template + bool push_with(Func f) + { + return enqueue_with(f); + } + + + + template + bool dequeue_with(Func f) + { + node *old_head = dequeue_head(); + + if (!old_head) + { + return false; + } + + f(*old_head->data); + delete old_head; + + return true; + } + + template + bool pop_with(Func f) + { + return dequeue_with(f); + } + + + + bool dequeue(value_type& destination) + { + return dequeue_with([&destination](value_type& src) { destination = src; }); + } + + bool pop(value_type& destination) + { + return dequeue(destination); + } + + void clear() + { + value_type v; + while (dequeue(v)); + } + + bool empty() const + { + return head.load() == tail.load(); + } + + size_t size() const + { + return itemCounter.value(); + } + + template + bool emplace(Args&&... args) + { + value_type val(std::forward(args)...); + return enqueue(val); + } + + std::nullptr_t statistics() const + { + return nullptr; + } + }; + }; +} + +#endif // #ifndef CDSLIB_CONTAINER_WilliamsQueueSPSC_H diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index 37446d066..1601eac3d 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -756,6 +756,8 @@ + + diff --git a/projects/Win/vc141/cds.vcxproj.filters b/projects/Win/vc141/cds.vcxproj.filters index 04f33e003..733ed574d 100644 --- a/projects/Win/vc141/cds.vcxproj.filters +++ b/projects/Win/vc141/cds.vcxproj.filters @@ -1246,6 +1246,12 @@ Header Files\cds\details + + Header Files\cds\container + + + Header Files\cds\container + Header Files\cds\compiler\gcc\arm8 diff --git a/projects/Win/vc141/gtest-queue.vcxproj b/projects/Win/vc141/gtest-queue.vcxproj index 51e8afefe..ef803e8d7 100644 --- a/projects/Win/vc141/gtest-queue.vcxproj +++ b/projects/Win/vc141/gtest-queue.vcxproj @@ -73,6 +73,8 @@ + + diff --git a/projects/Win/vc141/gtest-queue.vcxproj.filters b/projects/Win/vc141/gtest-queue.vcxproj.filters index 692356f3e..030cfcd94 100644 --- a/projects/Win/vc141/gtest-queue.vcxproj.filters +++ b/projects/Win/vc141/gtest-queue.vcxproj.filters @@ -92,6 +92,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/test/stress/queue/pop.cpp b/test/stress/queue/pop.cpp index cd5ab8a55..cbbdc8635 100644 --- a/test/stress/queue/pop.cpp +++ b/test/stress/queue/pop.cpp @@ -174,7 +174,8 @@ namespace { CDSSTRESS_FCDeque( queue_pop ) CDSSTRESS_RWQueue( queue_pop ) CDSSTRESS_StdQueue( queue_pop ) - + CDSSTRESS_WilliamsQueue( queue_pop ) + #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ TEST_F( test_fixture, type_name ) \ diff --git a/test/stress/queue/push.cpp b/test/stress/queue/push.cpp index 65e4aee6b..3b6b8521f 100644 --- a/test/stress/queue/push.cpp +++ b/test/stress/queue/push.cpp @@ -177,6 +177,7 @@ namespace { CDSSTRESS_FCDeque( queue_push ) CDSSTRESS_RWQueue( queue_push ) CDSSTRESS_StdQueue( queue_push ) + CDSSTRESS_WilliamsQueue( queue_push ) #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ diff --git a/test/stress/queue/push_pop.cpp b/test/stress/queue/push_pop.cpp index 148fde413..958250749 100644 --- a/test/stress/queue/push_pop.cpp +++ b/test/stress/queue/push_pop.cpp @@ -354,6 +354,7 @@ namespace { CDSSTRESS_FCDeque_HeavyValue( fc_with_heavy_value ) CDSSTRESS_RWQueue( simple_queue_push_pop ) CDSSTRESS_StdQueue( simple_queue_push_pop ) + CDSSTRESS_WilliamsQueue( simple_queue_push_pop ) #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ diff --git a/test/stress/queue/queue_type.h b/test/stress/queue/queue_type.h index c20770834..104958841 100644 --- a/test/stress/queue/queue_type.h +++ b/test/stress/queue/queue_type.h @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -423,6 +425,28 @@ namespace fc_details{ {}; typedef cds::container::RWQueue< Value, traits_RWQueue_mutex > RWQueue_mutex; + // WilliamsQueue + typedef cds::container::WilliamsQueue< Value > WilliamsQueue_default; + + struct traits_WilliamsQueue_ic : + public cds::container::williams_queue::make_traits< + cds::opt::item_counter + >::type + {}; + + typedef cds::container::WilliamsQueue< Value, traits_WilliamsQueue_ic > WilliamsQueue_ic; + + // WilliamsQueueSPSC + typedef cds::container::WilliamsQueueSPSC< Value > WilliamsQueueSPSC_default; + + struct traits_WilliamsQueueSPSC_ic : + public cds::container::williams_queue_spsc::make_traits< + cds::opt::item_counter + >::type + {}; + + typedef cds::container::WilliamsQueueSPSC< Value, traits_WilliamsQueueSPSC_ic > WilliamsQueueSPSC_ic; + // FCQueue struct traits_FCQueue_stat: public cds::container::fcqueue::make_traits< @@ -861,6 +885,14 @@ namespace cds_test { CDSSTRESS_Queue_F( test_fixture, RWQueue_mutex ) \ CDSSTRESS_RWQueue_1( test_fixture ) +#define CDSSTRESS_WilliamsQueue( test_fixture ) \ + CDSSTRESS_Queue_F( test_fixture, WilliamsQueue_default ) \ + CDSSTRESS_Queue_F( test_fixture, WilliamsQueue_ic ) + +#define CDSSTRESS_WilliamsQueueSPSC( test_fixture ) \ + CDSSTRESS_Queue_F( test_fixture, WilliamsQueueSPSC_default ) \ + CDSSTRESS_Queue_F( test_fixture, WilliamsQueueSPSC_ic ) + #define CDSSTRESS_SegmentedQueue( test_fixture ) \ CDSSTRESS_Queue_F( test_fixture, SegmentedQueue_HP_spin ) \ CDSSTRESS_Queue_F( test_fixture, SegmentedQueue_HP_spin_padding ) \ diff --git a/test/stress/queue/random.cpp b/test/stress/queue/random.cpp index 7c5dea9b7..c3cb79464 100644 --- a/test/stress/queue/random.cpp +++ b/test/stress/queue/random.cpp @@ -241,6 +241,7 @@ namespace { CDSSTRESS_FCDeque( queue_random ) CDSSTRESS_RWQueue( queue_random ) CDSSTRESS_StdQueue( queue_random ) + CDSSTRESS_WilliamsQueue( queue_random ) #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ diff --git a/test/stress/queue/spsc_queue.cpp b/test/stress/queue/spsc_queue.cpp index 4fa440a61..ea633af78 100644 --- a/test/stress/queue/spsc_queue.cpp +++ b/test/stress/queue/spsc_queue.cpp @@ -208,7 +208,8 @@ namespace { //CDSSTRESS_FCDeque( spsc_queue ) //CDSSTRESS_RWQueue( spsc_queue ) //CDSSTRESS_StdQueue( spsc_queue ) - + CDSSTRESS_WilliamsQueueSPSC(spsc_queue) + #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ TEST_F( test_fixture, type_name ) \ diff --git a/test/unit/queue/CMakeLists.txt b/test/unit/queue/CMakeLists.txt index aea3e81f4..86ac8af2e 100644 --- a/test/unit/queue/CMakeLists.txt +++ b/test/unit/queue/CMakeLists.txt @@ -2,7 +2,7 @@ set(PACKAGE_NAME unit-queue) set(CDSGTEST_QUEUE_SOURCES ../main.cpp - basket_queue_hp.cpp + basket_queue_hp.cpp basket_queue_dhp.cpp fcqueue.cpp moirqueue_hp.cpp @@ -16,6 +16,8 @@ set(CDSGTEST_QUEUE_SOURCES segmented_queue_dhp.cpp vyukov_mpmc_queue.cpp weak_ringbuffer.cpp + williams_queue.cpp + williams_queue_spsc.cpp intrusive_basket_queue_hp.cpp intrusive_basket_queue_dhp.cpp intrusive_fcqueue.cpp diff --git a/test/unit/queue/williams_queue.cpp b/test/unit/queue/williams_queue.cpp new file mode 100644 index 000000000..382e80f4d --- /dev/null +++ b/test/unit/queue/williams_queue.cpp @@ -0,0 +1,52 @@ +#include "test_generic_queue.h" + +#include + +namespace { + namespace cc = cds::container; + + class WilliamsQueue : public cds_test::generic_queue + {}; + + TEST_F(WilliamsQueue, defaulted) + { + typedef cds::container::WilliamsQueue< int > test_queue; + + test_queue q; + test(q); + } + + TEST_F(WilliamsQueue, item_counting) + { + typedef cds::container::WilliamsQueue< int, + typename cds::container::williams_queue::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + > ::type + > test_queue; + + test_queue q; + test(q); + } + + TEST_F(WilliamsQueue, move) + { + typedef cds::container::WilliamsQueue< std::string > test_queue; + + test_queue q; + test_string(q); + } + + TEST_F(WilliamsQueue, move_item_counting) + { + struct traits : public cc::williams_queue::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::WilliamsQueue< std::string, traits > test_queue; + + test_queue q; + test_string(q); + } + +} // namespace + diff --git a/test/unit/queue/williams_queue_spsc.cpp b/test/unit/queue/williams_queue_spsc.cpp new file mode 100644 index 000000000..1f5cd4af3 --- /dev/null +++ b/test/unit/queue/williams_queue_spsc.cpp @@ -0,0 +1,32 @@ +#include "test_generic_queue.h" + +#include + +namespace { + namespace cc = cds::container; + + class WilliamsQueueSPSC : public cds_test::generic_queue + {}; + + TEST_F(WilliamsQueueSPSC, defaulted) + { + typedef cc::WilliamsQueueSPSC< int > test_queue; + + test_queue q; + test(q); + } + + TEST_F(WilliamsQueueSPSC, item_counting) + { + typedef cds::container::WilliamsQueueSPSC< int, + typename cds::container::williams_queue_spsc::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + > ::type + > test_queue; + + test_queue q; + test(q); + } + +} // namespace +