5

I'm trying to substitute boost::lockfree::queue for std::queue in this file https://github.com/zaphoyd/websocketpp/blob/experimental/examples/broadcast_server/broadcast_server.cpp

I've added #include <boost/lockfree/queue.hpp>; changed line 130, std::queue<action> m_actions;, to boost::lockfree::queue<action> m_actions;; removed all lines having to do with locking; and changed line 103, m_actions.pop();, to m_actions.pop(a);.

I get these errors when I scons broadcast_server_lockfree in the project root after adding broadcast_server_lockfree = SConscript('#/broadcast_server_lockfree/SConscript',variant_dir = builddir + 'broadcast_server_lockfree',duplicate = 0) to the project root's SConstruct and using broadcast_server's SConstruct in the broadcast_server_lockfree directory:

root@server:~/websocketpp-experimental# scons broadcast_server_lockfree
scons: Reading SConscript files ...
C++11 build environment partially enabled
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build/release/broadcast_server_lockfree
g++ -o build/release/broadcast_server_lockfree/broadcast_server_lockfree.o -c -std=c++0x -Wall -Wcast-align -isystem /root/boost_1_53_0 -DNDEBUG -D_WEBSOCKETPP_CPP11_MEMORY_ -D_WEBSOCKETPP_CPP11_FUNCTIONAL_ -D_WEBSOCKETPP_CPP11_SYSTEM_ERROR_ -D_WEBSOCKETPP_CPP11_RANDOM_DEVICE_ -D_WEBSOCKETPP_NOEXCEPT_ -I. broadcast_server_lockfree/broadcast_server_lockfree.cpp
In file included from broadcast_server_lockfree/broadcast_server_lockfree.cpp:10:0:
/root/boost_1_53_0/boost/lockfree/queue.hpp: In instantiation of 'class boost::lockfree::queue<action>':
broadcast_server_lockfree/broadcast_server_lockfree.cpp:139:36:   required from here
/root/boost_1_53_0/boost/lockfree/queue.hpp:79:5: error: static assertion failed: (boost::has_trivial_destructor<T>::value)
/root/boost_1_53_0/boost/lockfree/queue.hpp:83:5: error: static assertion failed: (boost::has_trivial_assign<T>::value)
broadcast_server_lockfree/broadcast_server_lockfree.cpp: In member function 'void broadcast_server::process_messages()':
broadcast_server_lockfree/broadcast_server_lockfree.cpp:111:34: error: 'class boost::lockfree::queue<action>' has no member named 'front'
broadcast_server_lockfree/broadcast_server_lockfree.cpp:117:55: error: 'm_connection_lock' was not declared in this scope
broadcast_server_lockfree/broadcast_server_lockfree.cpp:120:55: error: 'm_connection_lock' was not declared in this scope
broadcast_server_lockfree/broadcast_server_lockfree.cpp:123:55: error: 'm_connection_lock' was not declared in this scope
In file included from broadcast_server_lockfree/broadcast_server_lockfree.cpp:10:0:
/root/boost_1_53_0/boost/lockfree/queue.hpp: In instantiation of 'boost::lockfree::queue<T, A0, A1, A2>::~queue() [with T = action; A0 = boost::parameter::void_; A1 = boost::parameter::void_; A2 = boost::parameter::void_]':
broadcast_server_lockfree/broadcast_server_lockfree.cpp:41:24:   required from here
/root/boost_1_53_0/boost/lockfree/queue.hpp:229:11: error: no matching function for call to 'action::action()'
/root/boost_1_53_0/boost/lockfree/queue.hpp:229:11: note: candidates are:
broadcast_server_lockfree/broadcast_server_lockfree.cpp:32:5: note: action::action(action_type, websocketpp::endpoint<websocketpp::connection<websocketpp::config::asio>, websocketpp::config::asio>::message_ptr)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:32:5: note:   candidate expects 2 arguments, 0 provided
broadcast_server_lockfree/broadcast_server_lockfree.cpp:31:5: note: action::action(action_type, websocketpp::connection_hdl)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:31:5: note:   candidate expects 2 arguments, 0 provided
broadcast_server_lockfree/broadcast_server_lockfree.cpp:30:8: note: action::action(const action&)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:30:8: note:   candidate expects 1 argument, 0 provided
In file included from broadcast_server_lockfree/broadcast_server_lockfree.cpp:10:0:
/root/boost_1_53_0/boost/lockfree/queue.hpp: In instantiation of 'boost::lockfree::queue<T, A0, A1, A2>::node::node(boost::lockfree::queue<T, A0, A1, A2>::node::handle_type) [with T = action; A0 = boost::parameter::void_; A1 = boost::parameter::void_; A2 = boost::parameter::void_; boost::lockfree::queue<T, A0, A1, A2>::node::handle_type = boost::lockfree::queue<action>::node*]':
/root/boost_1_53_0/boost/lockfree/detail/freelist.hpp:82:13:   required from 'T* boost::lockfree::detail::freelist_stack<T, Alloc>::construct(const ArgumentType&) [with bool ThreadSafe = true; bool Bounded = false; ArgumentType = boost::lockfree::queue<action>::node*; T = boost::lockfree::queue<action>::node; Alloc = std::allocator<boost::lockfree::queue<action>::node>]'
/root/boost_1_53_0/boost/lockfree/queue.hpp:126:75:   required from 'void boost::lockfree::queue<T, A0, A1, A2>::initialize() [with T = action; A0 = boost::parameter::void_; A1 = boost::parameter::void_; A2 = boost::parameter::void_]'
/root/boost_1_53_0/boost/lockfree/queue.hpp:166:9:   required from 'boost::lockfree::queue<T, A0, A1, A2>::queue() [with T = action; A0 = boost::parameter::void_; A1 = boost::parameter::void_; A2 = boost::parameter::void_]'
broadcast_server_lockfree/broadcast_server_lockfree.cpp:41:24:   required from here
/root/boost_1_53_0/boost/lockfree/queue.hpp:109:52: error: no matching function for call to 'action::action()'
/root/boost_1_53_0/boost/lockfree/queue.hpp:109:52: note: candidates are:
broadcast_server_lockfree/broadcast_server_lockfree.cpp:32:5: note: action::action(action_type, websocketpp::endpoint<websocketpp::connection<websocketpp::config::asio>, websocketpp::config::asio>::message_ptr)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:32:5: note:   candidate expects 2 arguments, 0 provided
broadcast_server_lockfree/broadcast_server_lockfree.cpp:31:5: note: action::action(action_type, websocketpp::connection_hdl)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:31:5: note:   candidate expects 2 arguments, 0 provided
broadcast_server_lockfree/broadcast_server_lockfree.cpp:30:8: note: action::action(const action&)
broadcast_server_lockfree/broadcast_server_lockfree.cpp:30:8: note:   candidate expects 1 argument, 0 provided
scons: *** [build/release/broadcast_server_lockfree/broadcast_server_lockfree.o] Error 1
scons: building terminated because of errors.

I know next to nothing about c++, and searches on the error have yielded nothing (since I have no idea what I'm reading).

Here's the boost::lockfree::queue example if it helps. http://boost-sandbox.sourceforge.net/doc/html/lockfree/examples.html

Please show me how to correct this.

action

struct action {
    action(action_type t, connection_hdl h) : type(t), hdl(h) {}
    action(action_type t, server::message_ptr m) : type(t), msg(m) {}

    action_type type;
    websocketpp::connection_hdl hdl;
    server::message_ptr msg;
};

3 Answers3

9

From the docs:

T must have a copy constructor, a trivial assignment operator, and a trivial destructor.

Thus your action class must look like the following:

class action
{
public:
    action(const action& rhs) { ... }
    //Implicitly defined destructor for itself and all member variables
    //Implicitly defined operator= for itself and all member variables

};

The static_asserts are complaining because your destructor and operator= are not implicitly defined by the compiler (or this is the case for at least one member variable of action).

Edit: I've had a quick look at the repo - I can't seem to find connection_hdl, and there is no message_ptr in template <typename endpoint> class server. Either way, one of websocketpp::connection_hdl hdl or server::message_ptr msg does not satisfy the above conditions. Just "dropping in" a lockfree queue instead of using a std::queue is likely a non-trivial task which will require a number of changes.

Yuushi
  • 25,132
  • 7
  • 63
  • 81
  • Thank-you! Basically, I need to figure out how to convert `action` from a `struct` to `class`? –  Mar 18 '13 at 01:17
  • No. `struct` and `class` are the exact same thing in C++, except for default access specifiers (everything in a `struct` is default `public`, while everything in a `class` is default `private`). Without seeing `action`, there's no way of telling you how to fix this. However, utilizing boost generally requires a reasonable knowledge of C++ in the first place - you are going to struggle immensely otherwise. – Yuushi Mar 18 '13 at 01:20
  • Thanks again! `action`'s in the first link, `line 29`. I am more than fine with struggling through c++ to get all of the performance increases so long as there are great stack members like you willing to help. Without stack, I'd be condemned to something slower with less features like php ajax. –  Mar 18 '13 at 01:27
  • I've edited my post. This seems non-trivial. Unless someone is very familiar with this code, I doubt you'll get an answer telling you exactly what to do. – Yuushi Mar 18 '13 at 02:00
  • This *may* be interesting: I'm trying to develop on windows for ease of development. Of course, I had to include `websocketpp`. What was strange was that I forgot to install boost. After including `websocketpp` but not boost, visual studio 2010 pro identified everything *except* `connection_hdl`. After I included and linked boost, vs10 recognized it. What do you think? –  Mar 29 '13 at 01:16
  • @Yuushi Thank you for this answer. It would appear std::function fails this test as well. Does a workaround/wrapper design exist that allows me to "give" a class a trivial destructor? – fbrereto May 15 '13 at 06:33
  • 1
    @fbrereto Nope. This post goes into some depth - http://stackoverflow.com/questions/3899223/what-is-a-non-trivial-constructor-in-c - but suffice to say, "trivial" is a recursive requirement - to be trivial *x*, all member variables must also be trivial *x*. – Yuushi May 15 '13 at 06:48
1

This question has been answer well by the above posting. Here I will provide more examples. The fruitful discussion gave a lots of guidance please read them. The real matter is the requirement for the template type of the queue. For practicality, let's look at some real use cases.

namespace lkf=boost::lockfree;
vector<lkf::spsc_queue<pair<Fastq*,int>, lkf::capacity<100>>> datastore;
//lkf::queue<pair<char*,char*>, lkf::capacity<100>> pile1, pile2;  //failed assert
lkf::queue<pair<char*,char*>*, lkf::capacity<100>> pile1, pile2;

The first line in the code passed the compiler (because it is spac_queue), but the second line pair failed the compiler (due to boost::ASSERT):

/usr/local/include/boost/lockfree/queue.hpp:99:5: error: static assertion failed: (boost::has_trivial_assign<T>::value)
     BOOST_STATIC_ASSERT((boost::has_trivial_assign<T>::value));

After converting pair to pointer type (line 3), the compiler is happy.

Could be boost assertion needs to be updated? Converting to pointer is one quick trick, may not be the best.

The boost lockfree::queue has three Requirements:

  1. T must have a copy constructor
  2. T must have a trivial assignment operator
  3. T must have a trivial destructor

spsc_queue has two:

  1. T must have a default constructor
  2. T must be copyable
Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
  • 1
    `spsc_queue` is a different type from `queue`. `spsc_queue` is a ring-buffer instead of some kind of linked list(?) that can optionally allocate more nodes. Anyway, presumably they just have different checks, whether they need those checks or not. – Peter Cordes Mar 29 '19 at 21:37
  • But more importantly, the queue type doesn't need to be *atomically* copyable. `pop` doesn't read the data out of a node until after claiming it, and `push` doesn't add a node to the queue until after constructing it with the data. (Or for a ring-buffer, until after claiming that slot so no other writer will step on it.) I haven't checked boost's source specifically, but that's how normal lockless queues work in general. The queue entry management uses lockless atomic ops, but the data can be large as long as its trivially copyable. – Peter Cordes Mar 29 '19 at 21:41
  • lockfree can be implemented with std::atomic_flag – Kemin Zhou Mar 29 '19 at 21:53
  • `std::pair` is not trivially-copyable. https://godbolt.org/z/ly98DK shows that there is an `operator=` function that only inlines and optimizes away with optimization enabled. It does just copy the object representation, but that's not what C++ "trivially copyable" means, unfortunately. https://en.cppreference.com/w/cpp/utility/pair says that a pair is trivially destructible of both its member types are, but doesn't unfortunately guarantee that trivially copyable members result in a trivially copyable pair. – Peter Cordes Mar 29 '19 at 22:01
  • I think you're misunderstanding lock freedom. If your only lock-free atomic type is `atomic_flag`, you can implement *locks*, and thus implement `std::atomic` for arbitrary `T`, but it *won't* be lock-free, just atomic. – Peter Cordes Mar 29 '19 at 22:02
  • `lkf::queue` works with a `struct custom_pair{char *a,*b;};`. https://godbolt.org/z/wQV5LU. Even adding a large array to the struct doesn't cause a problem, so it's clearly nothing to do with `std::atomic::is_always_lock_free`. It's just the fact that the constructors and assignment operator are defaulted instead of explicit. – Peter Cordes Mar 29 '19 at 22:40
0

I got this error when I used std::string for the queue item type.

boost::lockfree::queue<string>(10000)

As many good people have pointed out in the answers here and here, string is not allowed to be used for boost::lockfree::queue.

It does compiled with char *

boost::lockfree::queue<char*>(10000)

Yinon_90
  • 1,315
  • 12
  • 17