libfastcommon/src/fc_queue.c

344 lines
8.2 KiB
C

/*
* Copyright (c) 2020 YuQing <384681@qq.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the Lesser GNU General Public License, version 3
* or later ("LGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the Lesser GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//fc_queue.c
#include "pthread_func.h"
#include "fc_queue.h"
int fc_queue_init(struct fc_queue *queue, const int next_ptr_offset)
{
int result;
if ((result=init_pthread_lock_cond_pair(&queue->lcp)) != 0)
{
return result;
}
queue->head = NULL;
queue->tail = NULL;
queue->next_ptr_offset = next_ptr_offset;
return 0;
}
void fc_queue_destroy(struct fc_queue *queue)
{
destroy_pthread_lock_cond_pair(&queue->lcp);
}
void fc_queue_push_ex(struct fc_queue *queue, void *data, bool *notify)
{
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
FC_QUEUE_NEXT_PTR(queue, data) = NULL;
if (queue->tail == NULL) {
queue->head = data;
*notify = true;
} else {
FC_QUEUE_NEXT_PTR(queue, queue->tail) = data;
*notify = false;
}
queue->tail = data;
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
}
static inline bool fc_queue_exists(struct fc_queue *queue, void *data)
{
void *current;
if (queue->head == NULL) {
return false;
}
current = queue->head;
do {
if (current == data) {
return true;
}
current = FC_QUEUE_NEXT_PTR(queue, current);
} while (current != NULL);
return false;
}
int fc_queue_push_with_check_ex(struct fc_queue *queue,
void *data, bool *notify)
{
int result;
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
if (fc_queue_exists(queue, data)) {
result = EEXIST;
*notify = false;
} else {
result = 0;
FC_QUEUE_NEXT_PTR(queue, data) = NULL;
if (queue->tail == NULL) {
queue->head = data;
*notify = true;
} else {
FC_QUEUE_NEXT_PTR(queue, queue->tail) = data;
*notify = false;
}
queue->tail = data;
}
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
return result;
}
void *fc_queue_pop_ex(struct fc_queue *queue, const bool blocked)
{
void *data;
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
do {
data = queue->head;
if (data == NULL) {
if (!blocked) {
break;
}
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
data = queue->head;
}
if (data != NULL) {
queue->head = FC_QUEUE_NEXT_PTR(queue, data);
if (queue->head == NULL) {
queue->tail = NULL;
}
}
} while (0);
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
return data;
}
void *fc_queue_pop_all_ex(struct fc_queue *queue, const bool blocked)
{
void *data;
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
do {
data = queue->head;
if (data == NULL) {
if (!blocked) {
break;
}
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
data = queue->head;
}
if (data != NULL) {
queue->head = queue->tail = NULL;
}
} while (0);
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
return data;
}
void fc_queue_push_queue_to_head_ex(struct fc_queue *queue,
struct fc_queue_info *qinfo, bool *notify)
{
if (qinfo->head == NULL) {
*notify = false;
return;
}
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
FC_QUEUE_NEXT_PTR(queue, qinfo->tail) = queue->head;
queue->head = qinfo->head;
if (queue->tail == NULL) {
queue->tail = qinfo->tail;
*notify = true;
} else {
*notify = false;
}
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
}
void fc_queue_push_queue_to_tail_ex(struct fc_queue *queue,
struct fc_queue_info *qinfo, bool *notify)
{
if (qinfo->head == NULL) {
*notify = false;
return;
}
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
if (queue->head == NULL) {
queue->head = qinfo->head;
*notify = true;
} else {
FC_QUEUE_NEXT_PTR(queue, queue->tail) = qinfo->head;
*notify = false;
}
queue->tail = qinfo->tail;
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
}
void fc_queue_pop_to_queue_ex(struct fc_queue *queue,
struct fc_queue_info *qinfo, const bool blocked)
{
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
if (queue->head == NULL) {
if (blocked) {
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
}
}
if (queue->head != NULL) {
qinfo->head = queue->head;
qinfo->tail = queue->tail;
queue->head = queue->tail = NULL;
} else {
qinfo->head = qinfo->tail = NULL;
}
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
}
void *fc_queue_timedpop(struct fc_queue *queue,
const int timeout, const int time_unit)
{
void *data;
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
data = queue->head;
if (data == NULL) {
fc_cond_timedwait(&queue->lcp, timeout, time_unit);
data = queue->head;
}
if (data != NULL) {
queue->head = FC_QUEUE_NEXT_PTR(queue, data);
if (queue->head == NULL) {
queue->tail = NULL;
}
}
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
return data;
}
void *fc_queue_timedpeek(struct fc_queue *queue,
const int timeout, const int time_unit)
{
void *data;
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
data = queue->head;
if (data == NULL) {
fc_cond_timedwait(&queue->lcp, timeout, time_unit);
data = queue->head;
}
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
return data;
}
int fc_queue_alloc_chain(struct fc_queue *queue, struct fast_mblock_man
*mblock, const int count, struct fc_queue_info *chain)
{
struct fast_mblock_node *node;
if ((node=fast_mblock_batch_alloc1(mblock, count)) == NULL) {
chain->head = chain->tail = NULL;
return ENOMEM;
}
chain->head = chain->tail = node->data;
while ((node=node->next) != NULL) {
FC_QUEUE_NEXT_PTR(queue, chain->tail) = node->data;
chain->tail = node->data;
}
FC_QUEUE_NEXT_PTR(queue, chain->tail) = NULL;
return 0;
}
int fc_queue_free_chain(struct fc_queue *queue, struct fast_mblock_man
*mblock, struct fc_queue_info *qinfo)
{
struct fast_mblock_node *previous;
struct fast_mblock_node *current;
struct fast_mblock_chain chain;
void *data;
if (qinfo->head == NULL) {
return 0;
}
chain.head = previous = fast_mblock_to_node_ptr(qinfo->head);
data = FC_QUEUE_NEXT_PTR(queue, qinfo->head);
while (data != NULL) {
current = fast_mblock_to_node_ptr(data);
previous->next = current;
previous = current;
data = FC_QUEUE_NEXT_PTR(queue, data);
}
previous->next = NULL;
chain.tail = previous;
return fast_mblock_batch_free(mblock, &chain);
}
int fc_queue_remove(struct fc_queue *queue, void *data)
{
void *previous;
void *current;
int result;
pthread_mutex_lock(&queue->lcp.lock);
if (queue->head == NULL)
{
result = ENOENT;
}
else if (queue->head == data)
{
queue->head = FC_QUEUE_NEXT_PTR(queue, queue->head);
if (queue->head == NULL)
{
queue->tail = NULL;
}
result = 0;
}
else
{
result = ENOENT;
previous = queue->head;
while ((current=FC_QUEUE_NEXT_PTR(queue, previous)) != NULL)
{
if (current == data)
{
FC_QUEUE_NEXT_PTR(queue, previous) =
FC_QUEUE_NEXT_PTR(queue, current);
if (queue->tail == current)
{
queue->tail = previous;
}
result = 0;
break;
}
previous = current;
}
}
pthread_mutex_unlock(&queue->lcp.lock);
return result;
}