Search code examples
c++qtmemoryshared

Shared memory ring buffer crashes


I wrote a shared memory ringbuffer with QT and I turns out it works great in a single process with one consumer and one writer. If I try to read data from a second process I the first one which connected crashes with a seg fault. Maybe I'm overlooking something?

The Header File

#ifndef SHAREDMEMORYRINGBUFFER_H
#define SHAREDMEMORYRINGBUFFER_H

#include <QObject>
#include <QSharedMemory>

class SharedMemoryRingBuffer : public QObject
{
    Q_OBJECT
public:
    struct SharedMemoryAttributes {
        int32_t readPosition;
        int32_t writePosition;
        int32_t size;
        int32_t* data;
    };
    explicit SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent = 0);
    ~SharedMemoryRingBuffer();
    int write(int32_t frame);
    int32_t read();

    int32_t length();
    int32_t readPosition();
    int32_t writePosition();

    int avaibleSize();
    int isEmpty();
    void empty();
    int isFull();



private:
    SharedMemoryAttributes *_attributes;
    int32_t _writePosition;
    int32_t _readPosition;
    int32_t _length;
    QSharedMemory *_sharedMemory;
    int _headSize;
signals:
    void readFrame(QString name);
public slots:
};

#endif // SHAREDMEMORYRINGBUFFER_H

And the CPP File:

#include "sharedmemoryringbuffer.h"
#include <QDebug>
#define DEBUGGINGNAME "[SharedMemoryRingbuffer]"

SharedMemoryRingBuffer::SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent) : QObject(parent)
{
    int sizeOfData = sizeof(int32_t) * (size);
    int sizeOfHeader = sizeof(SharedMemoryAttributes);
    _sharedMemory = new QSharedMemory(sharedMemoryName);

    if (!_sharedMemory->attach()) {
        if (!_sharedMemory->create(sizeOfData+sizeOfHeader, QSharedMemory::ReadWrite )) {
            qDebug() << DEBUGGINGNAME << "Could not create shared memory object, aborting ...";
            qDebug() << DEBUGGINGNAME << _sharedMemory->errorString();
            //TODO: exit call
            return;
        }
    }

    _sharedMemory->lock();
    _attributes = reinterpret_cast<SharedMemoryAttributes*>(_sharedMemory->data());
    _attributes->readPosition = 0;
    _attributes->writePosition = 0;
    _attributes->size = size + 1;
    _attributes->data = (int32_t*) _sharedMemory->data() + sizeOfHeader;
    _sharedMemory->unlock();
}

SharedMemoryRingBuffer::~SharedMemoryRingBuffer()
{
    _sharedMemory->detach();
    delete _sharedMemory;
}

int SharedMemoryRingBuffer::write(int32_t frame)
{
    _sharedMemory->lock();
    if ( !isFull() ) {
        _attributes->data[writePosition()] = frame;
        if (writePosition() + 1 >= _attributes->size) _attributes->writePosition = 0;
        else _attributes->writePosition += 1;
        _sharedMemory->unlock();
        return 1;
    }
    _sharedMemory->unlock();
    return 0;
}


/**
 * @brief SharedMemoryRingBuffer::read
 * @return
 * if the buffer is empty this functions returns the last readable value
 *
 */
int32_t SharedMemoryRingBuffer::read()
{
    _sharedMemory->lock();
    int32_t frame = _attributes->data[readPosition()];
    if ( readPosition() != writePosition() ) {
        if (readPosition() + 1 >= _attributes->size ) _attributes->readPosition = 0;
        else _attributes->readPosition += 1;
        emit readFrame(_sharedMemory->key());
    }
    _sharedMemory->unlock();
    return frame;
}

int32_t SharedMemoryRingBuffer::length()
{
    if(readPosition() <= writePosition()) {
        return writePosition() - readPosition();
    } else {
        return readPosition() - writePosition();
    }
}

int32_t SharedMemoryRingBuffer::readPosition()
{
    return _attributes->readPosition;
}

int32_t SharedMemoryRingBuffer::writePosition()
{
    return _attributes->writePosition;
}

int SharedMemoryRingBuffer::avaibleSize()
{
    return -1;
}

int SharedMemoryRingBuffer::isEmpty()
{
    if ( readPosition() == writePosition() ) return 1;
    else return 0;
}

void SharedMemoryRingBuffer::empty()
{

}

int SharedMemoryRingBuffer::isFull()
{
    if ((writePosition() + 1) % _attributes->size == readPosition()) return 1;
    else return 0;
}

Solution

  • So I found a solution which was pretty obvious. If a second process connects to to the shared memory it should NOT set the head and tail position to 0.

    This is the fixed code, which works well :)

    #include "sharedmemoryringbuffer.h"
    #include <QDebug>
    #define DEBUGGINGNAME "[SharedMemoryRingbuffer]"
    
    SharedMemoryRingBuffer::SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent) : QObject(parent)
    {
        int sizeOfData = sizeof(int32_t) * (size);
        int sizeOfHeader = sizeof(SharedMemoryAttributes);
        _sharedMemory = new QSharedMemory(sharedMemoryName);
    
        if (_sharedMemory->isAttached()) _sharedMemory->detach();
        if (!_sharedMemory->attach()) {
            _sharedMemory->lock();
            _attributes = reinterpret_cast<SharedMemoryAttributes*>(_sharedMemory->data());
            _attributes->readPosition = 0;
            _attributes->writePosition = 0;
            _attributes->size = size;
            _sharedMemory->unlock();
    
            if (!_sharedMemory->create(sizeOfData+sizeOfHeader, QSharedMemory::ReadWrite )) {
                qDebug() << DEBUGGINGNAME << "Could not create shared memory object, aborting ...";
                qDebug() << DEBUGGINGNAME << _sharedMemory->errorString();
                //TODO: exit call
                return;
            }
        }
    
        _attributes->data = (int32_t*) _sharedMemory->data() + sizeOfHeader;
    
    }
    
    SharedMemoryRingBuffer::~SharedMemoryRingBuffer()
    {
        _sharedMemory->detach();
        delete _sharedMemory;
    }
    
    int SharedMemoryRingBuffer::write(int32_t frame)
    {
        _sharedMemory->lock();
        if ( !isFull() ) {
            _attributes->data[writePosition()] = frame;
            if (writePosition() + 1 >= _attributes->size) _attributes->writePosition = 0;
            else _attributes->writePosition += 1;
            _sharedMemory->unlock();
            return 1;
        }
        _sharedMemory->unlock();
        return 0;
    }
    
    
    /**
     * @brief SharedMemoryRingBuffer::read
     * @return
     * if the buffer is empty this functions return the last readable value
     *
     */
    int32_t SharedMemoryRingBuffer::read()
    {
        _sharedMemory->lock();
        int32_t frame = _attributes->data[readPosition()];
        if ( readPosition() != writePosition() ) {
            if (readPosition() + 1 >= _attributes->size ) _attributes->readPosition = 0;
            else _attributes->readPosition += 1;
            emit readFrame(_sharedMemory->key());
        }
        _sharedMemory->unlock();
        return frame;
    }
    
    int32_t SharedMemoryRingBuffer::length()
    {
        if(readPosition() <= writePosition()) {
            return writePosition() - readPosition();
        } else {
            return readPosition() - writePosition();
        }
    }
    
    int32_t SharedMemoryRingBuffer::readPosition()
    {
        return _attributes->readPosition;
    }
    
    int32_t SharedMemoryRingBuffer::writePosition()
    {
        return _attributes->writePosition;
    }
    
    int SharedMemoryRingBuffer::avaibleSize()
    {
        return -1;
    }
    
    int SharedMemoryRingBuffer::isEmpty()
    {
        if ( readPosition() == writePosition() ) return 1;
        else return 0;
    }
    
    void SharedMemoryRingBuffer::empty()
    {
    
    }
    
    int SharedMemoryRingBuffer::isFull()
    {
        if ((writePosition() + 1) % _attributes->size == readPosition()) return 1;
        else return 0;
    }