简体   繁体   中英

Dynamic circular buffer queue

I'm trying to implement a thread-safe mcmp-queue, I'm doing this with a resizable circular buffer, but I'm running into bugs. Currently I'm just testing the following class by writing to the queue on one thread, and reading it on another; but I get unexpected outputs. I suspect my resizing is bugged, but I'm currently scratching my head as to what I'm doing wrong, any help would be appreciated.

template <typename T>
class queue
{
public:
    queue() = default;
    queue(std::size_t capacity) : _data(new T[capacity]), _capacity(capacity){};

    void enqueue(T const& item)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if (_full())
            _resize();

        _push(item);
    }

    std::optional<T> dequeue()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if (_empty())
            return std::nullopt;

        auto front = _front();
        _pop();
        return std::move(front);
    }

private:
    T *_data;
    std::size_t _capacity;
    std::size_t _head;
    std::size_t _tail;
    std::mutex _mutex;

    std::size_t _size() const
    {
        if(_tail < _head)
            return _capacity - _head + _tail;

        return _tail - _head;
    }

    bool _empty()
    {
        return _size() == 0;
    }

    void _push(T const& value)
    {
        _data[_tail] = value;
        _tail = (_tail + 1) % _capacity;
    }
    
    bool _full()
    {
        return _size() == _capacity;
    }

    void _resize()
    {
        auto new_capacity = (_capacity == 0) ? 2 : _capacity * 2;
        T* new_data = new T[new_capacity];

        if(_data)
        {
            // std::copy(_data, _data + _capacity, new_data);
            if (_tail < _head)
            {
                auto to_copy = (_data + _head) - (_data + _capacity);
                std::copy(_data + _head, _data + _capacity, new_data);
                std::copy(_data, _data + _head, new_data + to_copy);
            }
            else
            {
                //std::copy(_data + _head, _data + _capacity, new_data);
                std::copy(_data + _head, _data + _tail, new_data);
            }
            delete[] _data;
        }
        _data = new_data;
        _capacity = new_capacity;
        _head = 0;
    }

    T& _front()
    {
        return _data[_head];
    }

    void _pop()
    {
        _head = (_head + 1) % _capacity;
    }
};

Revised implementation that solves the problem I was originally having:

template <typename T>
class queue
{
public:

    void enqueue(T const& item)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if(_full())
            _resize();

        _data[_tail] = item;
        _tail = (_tail + 1) % _capacity;
    }

    std::optional<T> dequeue()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if(_empty())
            return std::nullopt;
        
        T item = std::move(_data[_head]);
        _head = (_head + 1) % _capacity;
        return item;
    }

private:
    T *_data;
    std::size_t _capacity;
    std::size_t _head;
    std::size_t _tail;
    std::mutex _mutex;

    bool _full()
    {
        return (_size() == _capacity);
    }

    std::size_t _size()
    {
        return (_head - _tail) & (_capacity - 1);
    }

    bool _empty()
    {
        return (_size() == 0);
    }

    void _resize()
    {
        auto new_capacity = (_capacity == 0) ? 2 : _capacity * 2;
        auto new_array = new T[new_capacity];

        auto size = _size();

        if (size > 0)
        {
            if(_head < _tail)
            {
                std::copy(_data + _head, _data + size, new_array);
            }
            else
            {
                std::copy(_data + _head, _data + (size - _head), new_array);
                std::copy(_data, _data + _tail, new_array + (size - _head));
            }
        }

        _capacity = new_capacity;
        _data = new_array;
        _head = 0;
        _tail = (size == _capacity) ? 0 : size;
    }
};
if (_tail < _head)
{
    auto to_copy = (_data + _head) - (_data + _capacity);

Is this correct? As written, looks like data cancels out, leaving head - capacity as your amount to copy, which can go negative...

            else
            {
                //std::copy(_data + _head, _data + _capacity, new_data);
                std::copy(_data + _head, _data + _tail, new_data);
            }
            delete[] _data;
        }
        _data = new_data;
        _capacity = new_capacity;
        _head = 0;

shouldn't tail index be also re-adjusted here? Assume tail==10, head==3, after copy head becomes 0, tail remains 10. (and I haven't analyzed the if condition being met).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM