Jonas K
Jonas K

Reputation: 168

Using unique_ptr member variables or not?

What is a better designpattern for creating a blocking bufferqueue, that uses resources efficiently in C++11/14 without too many allocs/moves? Using Queue<std::unique_ptr<Workitem>>

OR

Queue<Workitem> and with that hiding the resource management in the implementation (a bit like stl containers). Note, the second idea (Queue) is commented out. What are the implications of the commented out version also regarding heap/stack? And what about using std::unique_ptr<Queue<Workitem>> q?

I am not very good with c++, but regardless of the version, I can't really leak memory right? (reasonging: not new/delete -> no memoryleaks)

Code here:

#include <condition_variable>
#include <mutex>
#include <queue>

template <class T> class Queue {
public:
  Queue(size_t size);

  // push item to _queue if _queue.size() < _size
  // else block
  void push(T item);
  // void push(std::unique_ptr<T> item);

  // pop item if !_queue.empty()
  // else block or return false after timeout
  bool pop(T &item);
  // bool pop(std::unique_ptr<T> &item);

private:
  std::mutex _mutex;
  std::size_t _size;

  std::queue<T> _queue;
  // std::queue<std::unique_ptr<T>> _queue;

  std::condition_variable _condition_full;
  std::condition_variable _condition_empty;
};

struct Workitem {
  size_t idx;
  void *workdetails;
};

void do_work(Queue<std::unique_ptr<Workitem>> &work_q,
             Queue<std::unique_ptr<Workitem>> &write_q,
             struct options_s &opts) {
  std::unique_ptr<Workitem> work;
  while (work_q.pop(work)) {
    // calculation w/ work
    std::unique_ptr<Workitem> res = consume(work, opts);
    write_q.push(std::move(work));
  }
}
void do_write(Queue<std::unique_ptr<Workitem>> &write_q,
              struct options_s &opts) {
  std::unique_ptr<Workitem> work;
  while (write_q.pop(work)) {
    prepare_for_writing(work, opts); // clean item
    write(work);
  }
}

auto w1 = std::thread(do_work, std::ref(work_q), std::ref(write_q),
                      std::ref(options));
auto w2 = std::thread(do_work, std::ref(work_q), std::ref(write_q),
                      std::ref(options));
auto writer = std::thread(do_write, std::ref(write_q), std::ref(options));

int main() {
  Queue<std::unique_ptr<Workitem>> work_q{4};
  Queue<std::unique_ptr<Workitem>> write_q{4};
  // Queue<Workitem> q{4};
  // ??? std::unique_ptr<Queue<Workitem>> q{4} ???

  for (size_t i, ...) { // do many iterations
    std::unique_ptr<Workitem> w{};
    // Workitem w{};

    populate(w, i); // populate work item

    work_q.push(std::move(w));
  }

  w1.join();
  w2.join();
  writer.join();
}

I can give the implementation if that helps, I just didn't want to clutter everything so I left it out. As a remark, the queue is used by threads. I use two queues for multiple worker threads and one writer thread to spread the load across cores.

cheers

Upvotes: 0

Views: 776

Answers (1)

eerorika
eerorika

Reputation: 238311

What is a better designpattern for creating a blocking bufferqueue, that uses resources efficiently in C++11/14 without too many allocs/moves? Using Queue<std::unique_ptr<Workitem>> OR Queue<Workitem>

Given that criteria, Queue<Workitem> is better because it avoids one layer of dynamic allocation.

What are the implications of the commented out version also regarding heap/stack?

There aren't really any differences regarding heap versus stack. The objects are dynamically allocated in both variations.

I can't really leak memory right? (reasonging: not new/delete -> no memoryleaks)

Your reasoning is sound. The example code that you show has no memory leaks.

There however is a bare pointer member variable Workitem::workdetails that may have potential for memory leaks if it is misused (if it ever owns memory that it points to).

Upvotes: 2

Related Questions