dimanche 18 août 2019

Thread safe wrapper around std::queue doesn't build if storing unique_ptr, but std::queue works

I have a supposed thread safe blocking queue implementation which is supposed to be a wrapper around std::queue Below is its implementation:

template <typename _Tp>
class SharedQueue
{
  public:
    explicit SharedQueue(bool isBlocking = true) : isBlocking_(isBlocking) {}

    virtual ~SharedQueue() {}

    virtual const _Tp& front()
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        // if this is a blocking queue, wait to be notified when when a new object is added
        if (isBlocking_)
        {
            while (queue_.empty())
            {
                cv_.wait(mlock);
            }
        }

        return queue_.front();
    }

    virtual bool empty() const
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        return queue_.empty();
    }

    virtual size_t size() const
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        return queue_.size();
    }

    virtual void push(const _Tp& value)
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        queue_.push(value);

        if (isBlocking_)
        {
            if (queue_.size() == 1)
            {
                cv_.notify_all();
            }
        }
    }

    virtual void push(_Tp&& value)
    {
        {
            std::unique_lock<std::mutex> mlock(mtx_);

            queue_.push(std::move(value));

            if (isBlocking_)
            {
                if (queue_.size() == 1)
                {
                    cv_.notify_all();
                }
            }
        }
    }

    template <typename... _Args>
    void emplace(_Args&&... __args)
    {
        {
            std::unique_lock<std::mutex> mlock(mtx_);

            queue_.emplace(std::forward<_Args>(__args)...);

            if (isBlocking_)
            {
                if (queue_.size() == 1)
                {
                    cv_.notify_all();
                }
            }
        }
    }

    virtual void pop()
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        if (!queue_.empty())
        {
            queue_.pop();
        }
    }

  private:
    bool isBlocking_;

    mutable std::mutex mtx_;

    mutable std::condition_variable cv_;

    std::queue<_Tp> queue_;
};

I want to be be able to place unique_ptr's on this queue and I understand that I'd have to call std::move on the unique_ptr when pushing it on the queue from a client application. Here's my problem... In my main, when I create a std::queue as follows, my program compiles just fine

std::queue<std::unique_ptr<int32_t>> q1;

However, when I create an instance of my SharedQueue as below, the program does not compile.

SharedQueue<std::unique_ptr<int32_t>> q2;

I get an error telling me the copy constructor was deleted in the unique_ptr class, which is understandable. I guess I'm just wondering what std::queue does that the code can compile while my code 'appears' to be a wrapper around the std::queue and implements the operations similarly.

Aucun commentaire:

Enregistrer un commentaire