QT实现基于QIODevice的线程安全环形队列.
/******************************************************************************
Copyright (C) 2017-2018 Zhang Fei <1158114251@qq.com>
This class inherits the QIODevice class.
This class implements a lock-free ring buffer (mainly for efficiency),
noting that the lock-free condition is: a read thread, a write thread.
If it is multiple read threads and multiple write threads,
then you need two locks to lock the read and write threads.
The buffer will signal when it is full.
******************************************************************************/
#ifndef QRING_BUFFER_H
#define QRING_BUFFER_H
#include <QtCore/qiodevice.h>
#include <QtCore/qbytearray.h>
#include <atomic>
#include <QDebug>
namespace QIO1
{
class QRing_Buffer;
}
class QRing_Buffer : public QIODevice
{
Q_OBJECT
public:
QRing_Buffer();
~QRing_Buffer();
char * fifo_init(quint32 size);
quint32 fifo_in(const char *buffer, quint32 size);
quint32 fifo_out(char *buffer, quint32 size);
qint64 size() const Q_DECL_OVERRIDE;
qint64 pos() const Q_DECL_OVERRIDE;
bool seek(qint64 off) Q_DECL_OVERRIDE;
quint32 len;
signals:
void fifo_full(void);
void fifo_empty(void);
private slots:
void fifo_full_deal(void);
void fifo_empty_deal(void);
protected:
qint64 writeData(const char *buffer, qint64 sizel) Q_DECL_OVERRIDE;
qint64 readData(char *buffer, qint64 sizel) Q_DECL_OVERRIDE;
private:
quint32 in;
quint32 out;
char * buffer;
};
#endif // QRING_BUFFER_H
实现:
# include "qring_buffer.h"
# include <QDebug>
# define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
quint32 _min (quint32 a,quint32 b)
{
return a < b ? a : b;
}
QRing_Buffer::QRing_Buffer()
{
in = 0;
out = 0;
len=0;
buffer=NULL;
}
QRing_Buffer::~QRing_Buffer()
{
if(buffer)
free(buffer);
}
void QRing_Buffer::fifo_full_deal(void)
{
qDebug()<<"fifo full";
}
void QRing_Buffer::fifo_empty_deal(void)
{
qDebug()<<"fifo empty";
}
char * QRing_Buffer:: fifo_init(quint32 size)
{
if (!is_power_of_2(size))
{
qDebug("size must be power of 2.\n");
goto out;
}
if (buffer=(char *) malloc(size),!buffer)
{
qDebug("fail to malloc");
goto out;
}
memset(buffer,0,size); // malloc lazy
len=size;
QObject::connect(this,&QRing_Buffer::fifo_full,\
this,&QRing_Buffer::fifo_full_deal);
QObject::connect(this,&QRing_Buffer::fifo_empty,\
this,&QRing_Buffer::fifo_empty_deal);
return buffer;
out:
return NULL;
}
qint64 QRing_Buffer::size() const
{
return in - out;
}
qint64 QRing_Buffer::pos() const
{
return 0;
}
bool QRing_Buffer::seek(qint64 off)
{
return false;
}
quint32 QRing_Buffer::fifo_in(const char *buffer, quint32 size)
{
quint32 rsize;
if (rsize=writeData(buffer, size),rsize!=size)
emit fifo_full();
return rsize;
}
quint32 QRing_Buffer::fifo_out(char *buffer, quint32 size)
{
quint32 rsize;
if (rsize =readData(buffer, size),rsize!=size)
emit fifo_empty();
return rsize;
}
qint64 QRing_Buffer::writeData(const char *buffer, qint64 sizel)
{
quint32 l ;
if(sizel<0)
{
return 0;
}
quint32 size =(quint32) sizel;
size = _min(size, len - in + out);
l = _min(size, len - (in & (len - 1)));
memcpy(this->buffer + (in & (len - 1)), buffer, l);
memcpy(this->buffer, buffer + l, size - l);
/*
The memory barrier only guarantees that the cpu is not out of order,
and the index is updated after copying first.
*/
std::atomic_thread_fence(std::memory_order_acquire);
in += size;
return size;
}
qint64 QRing_Buffer::readData(char *buffer, qint64 sizel)
{
quint32 l ;
if(sizel<0)
{
return 0;
}
quint32 size =(quint32) sizel;
size= _min(size,in-out);
l = _min(size, len - (out & (len - 1)));
memcpy(buffer, this->buffer + (out & (len - 1)), l);
memcpy(buffer + l, this->buffer, size - l);
std::atomic_thread_fence(std::memory_order_acquire);
out += size;
return size;
}