1

Consider the following code

struct foo
{
    foo()
    {
        size_t someCalculatedValue = 2;
        bar.resize(someCalculatedValue*4);
        std::generate(bar.begin(), bar.end(), [&someCalculatedValue]() {return boost::lockfree::queue<int>(0xFFFF * someCalculatedValue); });
    }
    std::vector<boost::lockfree::queue<int>> bar;

};

which wouldnt compile, complaining about deleted copy constructor. The queue is non-copyable, which is ok, but looks like it also non movable? Am I missing something? Is there a way to fill stl container with these?
Of course, one can use something like below, if the capacity of 64k is enough.

struct boo
{
    using LocklessQueue = boost::lockfree::queue<int, boost::lockfree::capacity<0xFFFF-1>>;
    boo()
    {
        size_t someCalculatedValue = 2;
        bar = std::vector<LocklessQueue>(someCalculatedValue*4);
    }
    std::vector<LocklessQueue> bar;

};
kreuzerkrieg
  • 3,009
  • 3
  • 28
  • 59
  • Do you really need the queues to be dynamically allocated separately from `struct foo` itself? Can't you make the size a compile-time constant and use a plain array? – Peter Cordes Feb 13 '18 at 18:46
  • nope. I think the `someCalculatedValue` shows exactly the problem, something should be calculated prior to deciding the queue size, otherwise there would be no problem to init it at compile time – kreuzerkrieg Feb 13 '18 at 19:20
  • Does C++ allow a flexible-array member at the end of a struct? I think GNU C++ does as an extension, at least, if that helps. You could use `struct foo{ int dummy; boost::lockfree::queue queues[]; };` and then manually construct the elements of `queues[]` with placement-new. – Peter Cordes Feb 13 '18 at 19:37
  • Related / possible duplicate: [Container of fixed dynamic size](https://stackoverflow.com/questions/14895052/container-of-fixed-dynamic-size) – Peter Cordes Feb 13 '18 at 19:37
  • FAM? Not a standard in C++ and IMO it never should be there. It was born in C domain and should stay there – kreuzerkrieg Feb 13 '18 at 19:47
  • Yeah, ugly as hell, but if you were already dynamically allocating `struct foo`, it would compile more efficiently (with one fewer levels of indirection than any container that allocates separate storage). If you're not, then it would just be a bad idea. – Peter Cordes Feb 13 '18 at 19:55
  • I allocate it once and only once during the lifetime of application, so, indeed I can afford allocate it dynamically. on the other hand, @sehe proposed more elegant solution, which is not a complicated task, given the uglicity of FAM – kreuzerkrieg Feb 14 '18 at 06:52

1 Answers1

2

Locking primitives and lockfree objects are rarely movable.

This makes sense, because, by definition they're intended to be shared. When sharing, the object identity must stay the same, otherwise one party might move the object while the other is trying to still access it in the old location.

In the case of your code sample, I'm not convinced you have a need for lockless containers.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • yes, indeed, struct boo cannot benefit from lockless whatever :). in fact it is a container which holds lockless elements which represent submission queue for a physical device(s) where producers are going to submit their work, lockless queue sounds like a solution when you have to submit 1m of "tasks" per second – kreuzerkrieg Feb 13 '18 at 18:16
  • movability during the lifetime of lockless object is indeed makes no sense, on the other hand, how one populates a set of lockless objects, with no intention to copy/move it further, except storing pointers? – kreuzerkrieg Feb 13 '18 at 18:19
  • 1
    Use a container with reference stability. Boost has stable_vector but you can use any node based container like std::list. Be sure to emplace the element. – sehe Feb 13 '18 at 18:25
  • hm... do you think node based solution is suitable when microseconds matter? well, on the other hand, if the list is short enough, it is cheap, but makes the code uglier. Definitely will consider this option – kreuzerkrieg Feb 13 '18 at 19:10
  • The part is not cheaper if it's shorter, unless you need to traverse it for sine reason (use a map?). Also if microseconds matter you're crazy for creating queues all the time. I assumed that creating queues (for a new device.?) would be a relatively infrequent event. Otherwise use fewer queues (one per thread?) or pool the instances. – sehe Feb 13 '18 at 20:00
  • no,no,no, you got it wrong, the pool (the container) of queues created once, then when request comes in I have to locate the right entry in container and push the request there. since the corresponding queue in container was picked based on modulus, vector was a natural choice to use the modulus as input of the index-of operator. BTW, after good sleep, I realized that I dont get why node based container matters? – kreuzerkrieg Feb 14 '18 at 06:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/165111/discussion-between-sehe-and-kreuzerkrieg). – sehe Feb 14 '18 at 07:47