看代码的涉及风格是面向对象设计。先设计接口,然后不同的平台去做对应的实现。
接口定义
code-1.0/foundation/distributedschedule/services/samgr_lite/samgr/adapter/queue_adapter.h
MQueueId QUEUE_Create(const char *name, int size, int count);
int QUEUE_Put(MQueueId queueId, const void *element, uint8 pri, int timeout);
int QUEUE_Pop(MQueueId queueId, void *element, uint8 *pri, int timeout);
int QUEUE_Destroy(MQueueId queueId);
队列定义了四个接口,分别是创建队列、添加元素到队列、从队列取出元素、销毁队列。
队列的实现上涉及两个概念无锁队列(LockFreeQueue)和加锁队列(LockFreeBlockQueue)。
LockFreeBlockQueue
是在LockFreeQueue
的基础上增加锁。
LockFreeQueue
code-1.0/foundation/distributedschedule/services/samgr_lite/samgr/adapter/posix/lock_free_queue.h
struct LockFreeQueue {
uint32 write; // 写游标
uint32 read; // 读游标
uint32 itemSize; // 一个元素的大小
uint32 totalSize; // 缓冲区大小
uint8 buffer[0]; // 缓冲区指针
};
LockFreeQueue *LFQUE_Create(int size, int count);
BOOL LFQUE_IsEmpty(LockFreeQueue *queue);
BOOL LFQUE_IsFull(LockFreeQueue *queue);
int LFQUE_Push(LockFreeQueue *queue, const void *element, uint8 pri);
int LFQUE_Pop(LockFreeQueue *queue, void *element, uint8 *pri);
LockFreeQueue
数据结构定义和接口定义。定义接口:创建队列、获取队列是否空、获取队列是否满、元素入队列、元素出队列。
code-1.0/foundation/distributedschedule/services/samgr_lite/samgr/adapter/posix/lock_free_queue.c
LockFreeQueue创建
// 传入参数:元素的大小和个数
LockFreeQueue *LFQUE_Create(int size, int count)
{
// 计算buffer总大小
// 为啥还要加1?
int total = size * count + 1;
if (total <= 0) {
return NULL;
}
// 申请内存
register LockFreeQueue *queue = (LockFreeQueue *)SAMGR_Malloc(sizeof(LockFreeQueue) + total);
if (queue == NULL) {
return NULL;
}
// 设置读写游标为起始位置
queue->write = 0;
queue->read = 0;
// 设置元素大小
queue->itemSize = size;
// 设置buffer总大小
queue->totalSize = total;
return queue;
}
计算buffer时,多加了个1,是因为:队列设计需要。队列设计的一条是:写入一个元素后,写游标移动一个元素的长度。读写游标重合表示队列空,写游标的下一个位置是读游标表示队列满。读游标永远指向队列数据的开始位置,写游标永远位于队列末尾数据的下一个位置。即,写游标永远指向一个未写入数据的位置。所以buffer的长度要在总元素长度的基础上加一个写游标的位置(1个字节)。
判断队列是空还是满
BOOL LFQUE_IsFull(LockFreeQueue *queue)
{
uint32 nextWrite = queue->write + 1;
if (nextWrite >= queue->totalSize) {
nextWrite = 0;
}
return (nextWrite == queue->read);
}
BOOL LFQUE_IsEmpty(LockFreeQueue *queue)
{
return (queue->write == queue->read);
}
如果下一次写的位置和读的位置是同一个位置,则队列满。
如果写的位置和读的位置是同一个位置,则队列空。
入队列
int LFQUE_Push(LockFreeQueue *queue, const void *element, uint8 pri)
{
(void)pri;
if (queue == NULL || element == NULL) {
return EC_INVALID;
}
// 如果队列满了,直接返回,无法写入
if (LFQUE_IsFull(queue)) {
return EC_BUSBUSY;
}
// 将元素写入队列
uint32 copyLen = (queue->totalSize - queue->write < queue->itemSize) ?
(queue->totalSize - queue->write) : queue->itemSize;
if (memcpy_s(&queue->buffer[queue->write], copyLen, element, copyLen) != EOK) {
return EC_INVALID;
}
element += copyLen;
copyLen = queue->itemSize - copyLen;
if (copyLen > 0) {
if (memcpy_s(queue->buffer, copyLen, element, copyLen) != EOK) {
return EC_INVALID;
}
}
// 设置写游标
uint32 write = queue->write + queue->itemSize;
if (write >= queue->totalSize) {
write = write - queue->totalSize;
}
queue->write = write;
return EC_SUCCESS;
}
缓冲区是作为环形缓冲区使用,但是实际是一段内存,并非环形。写入数据的时候,要考虑是否写入到了这段内存的末尾,如果不够用,需要把剩余的部分写入到这段内存的开头,所以会出现上面比较多的代码操作元素写入。
出队列
int LFQUE_Pop(LockFreeQueue *queue, void *element, uint8 *pri)
{
(void)pri;
if (queue == NULL || element == NULL) {
return EC_INVALID;
}
// 判断队列是否为空,如果为空则直接返回
if (LFQUE_IsEmpty(queue)) {
return EC_FAILURE;
}
// 读取元素
uint32 copyLen = (queue->totalSize - queue->read < queue->itemSize) ?
(queue->totalSize - queue->read) : queue->itemSize;
if (memcpy_s(element, copyLen, &queue->buffer[queue->read], copyLen) != EOK) {
return EC_FAILURE;
}
element += copyLen;
copyLen = queue->itemSize - copyLen;
if (copyLen > 0) {
if (memcpy_s(element, copyLen, queue->buffer, copyLen) != EOK) {
return EC_FAILURE;
}
}
// 设置读游标
uint32 read = queue->read + queue->itemSize;
if (read >= queue->totalSize) {
read = read - queue->totalSize;
}
queue->read = read;
return EC_SUCCESS;
}
一段内存模拟的环形缓冲区,这里和元素入队列类似,都会涉及到达内存末端的情况。
LockFreeBlockQueue
code-1.0/foundation/distributedschedule/services/samgr_lite/samgr/adapter/posix/queue_adapter.c
struct LockFreeBlockQueue {
pthread_mutex_t wMutex;
pthread_mutex_t rMutex;
pthread_cond_t cond;
LockFreeQueue *queue;
};
在LockFreeQueue
的基础上增加了读写信号锁和一个条件变量。
创建LockFreeBlockQueue
MQueueId QUEUE_Create(const char *name, int size, int count)
{
// 分配内存
LockFreeBlockQueue *queue = (LockFreeBlockQueue *)SAMGR_Malloc(sizeof(LockFreeBlockQueue));
if (queue == NULL) {
return NULL;
}
// 创建LFQ,并赋值queue
queue->queue = LFQUE_Create(size, count);
if (queue->queue == NULL) {
SAMGR_Free(queue);
return NULL;
}
// 读写信号所初始化
pthread_mutex_init(&queue->wMutex, NULL);
pthread_mutex_init(&queue->rMutex, NULL);
// 条件变量初始化
pthread_cond_init(&queue->cond, NULL);
return (MQueueId)queue;
}
入队列
int QUEUE_Put(MQueueId queueId, const void *element, uint8 pri, int timeout)
{
if (queueId == NULL || element == NULL || timeout > 0) {
return EC_INVALID;
}
// 类型强转,void*类型转为LockFreeBlockQueue*类型
LockFreeBlockQueue *queue = (LockFreeBlockQueue *)queueId;
// 锁定wMutex
pthread_mutex_lock(&queue->wMutex);
// 将元素写入队列
int ret = LFQUE_Push(queue->queue, element, pri);
// 释放wMutex
pthread_mutex_unlock(&queue->wMutex);
// 锁定rMutex
pthread_mutex_lock(&queue->rMutex);
// 唤醒条件变量cond上的线程
pthread_cond_signal(&queue->cond);
// 释放rMutex
pthread_mutex_unlock(&queue->rMutex);
return ret;
}
出队列
int QUEUE_Pop(MQueueId queueId, void *element, uint8 *pri, int timeout)
{
if (queueId == NULL || element == NULL || timeout > 0) {
return EC_INVALID;
}
// 类型强转,void*类型转为LockFreeBlockQueue*类型
LockFreeBlockQueue *queue = (LockFreeBlockQueue *)queueId;
// 读取元素,这段代码的前后为什么不要操作rMutex
if (LFQUE_Pop(queue->queue, element, pri) == EC_SUCCESS) {
return EC_SUCCESS;
}
// 锁定rMutex
pthread_mutex_lock(&queue->rMutex);
// 如果读取元素不成功,说明队列空,进入等待状态
while (LFQUE_Pop(queue->queue, element, pri) != EC_SUCCESS) {
pthread_cond_wait(&queue->cond, &queue->rMutex);
}
// 释放rMutex
pthread_mutex_unlock(&queue->rMutex);
return EC_SUCCESS;
}
销毁队列
int QUEUE_Destroy(MQueueId queueId)
{
if (queueId == NULL) {
return EC_INVALID;
}
// 类型强转,void*类型转为LockFreeBlockQueue*类型
LockFreeBlockQueue *queue = (LockFreeBlockQueue *)queueId;
// 销毁读写信号锁和条件变量
pthread_mutex_destroy(&queue->wMutex);
pthread_mutex_destroy(&queue->rMutex);
pthread_cond_destroy(&queue->cond);
// 释放LFQ
SAMGR_Free(queue->queue);
// 释放队列指针
SAMGR_Free(queue);
return EC_SUCCESS;
}