195 lines
5.6 KiB
C
195 lines
5.6 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/>.
|
|
*/
|
|
|
|
//sorted_queue.c
|
|
|
|
#include "pthread_func.h"
|
|
#include "sorted_queue.h"
|
|
|
|
int sorted_queue_init(struct sorted_queue *sq, const int dlink_offset,
|
|
int (*push_compare_func)(const void *data1, const void *data2),
|
|
int (*pop_compare_func)(const void *data, const
|
|
void *less_equal, void *arg), void *arg)
|
|
{
|
|
int result;
|
|
|
|
if ((result=init_pthread_lock_cond_pair(&sq->lcp)) != 0) {
|
|
return result;
|
|
}
|
|
|
|
FC_INIT_LIST_HEAD(&sq->head);
|
|
sq->dlink_offset = dlink_offset;
|
|
sq->arg = arg;
|
|
sq->push_compare_func = push_compare_func;
|
|
sq->pop_compare_func = pop_compare_func;
|
|
return 0;
|
|
}
|
|
|
|
void sorted_queue_destroy(struct sorted_queue *sq)
|
|
{
|
|
destroy_pthread_lock_cond_pair(&sq->lcp);
|
|
}
|
|
|
|
void sorted_queue_push_ex(struct sorted_queue *sq, void *data, bool *notify)
|
|
{
|
|
struct fc_list_head *dlink;
|
|
struct fc_list_head *current;
|
|
|
|
dlink = FC_SORTED_QUEUE_DLINK_PTR(sq, data);
|
|
PTHREAD_MUTEX_LOCK(&sq->lcp.lock);
|
|
if (fc_list_empty(&sq->head)) {
|
|
fc_list_add(dlink, &sq->head);
|
|
*notify = true;
|
|
} else {
|
|
if (sq->push_compare_func(data, FC_SORTED_QUEUE_DATA_PTR(
|
|
sq, sq->head.prev)) >= 0)
|
|
{
|
|
fc_list_add_tail(dlink, &sq->head);
|
|
*notify = false;
|
|
} else if (sq->push_compare_func(data, FC_SORTED_QUEUE_DATA_PTR(
|
|
sq, sq->head.next)) < 0)
|
|
{
|
|
fc_list_add(dlink, &sq->head);
|
|
*notify = true;
|
|
} else {
|
|
current = sq->head.prev->prev;
|
|
while (sq->push_compare_func(data, FC_SORTED_QUEUE_DATA_PTR(
|
|
sq, current)) < 0)
|
|
{
|
|
current = current->prev;
|
|
}
|
|
fc_list_add_after(dlink, current);
|
|
*notify = false;
|
|
}
|
|
}
|
|
|
|
PTHREAD_MUTEX_UNLOCK(&sq->lcp.lock);
|
|
}
|
|
|
|
void *sorted_queue_pop_ex(struct sorted_queue *sq,
|
|
void *less_equal, const bool blocked)
|
|
{
|
|
void *data;
|
|
struct fc_list_head *current;
|
|
|
|
PTHREAD_MUTEX_LOCK(&sq->lcp.lock);
|
|
do {
|
|
if (fc_list_empty(&sq->head)) {
|
|
if (!blocked) {
|
|
data = NULL;
|
|
break;
|
|
}
|
|
|
|
pthread_cond_wait(&sq->lcp.cond,
|
|
&sq->lcp.lock);
|
|
if (fc_list_empty(&sq->head)) {
|
|
data = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
current = sq->head.next;
|
|
data = FC_SORTED_QUEUE_DATA_PTR(sq, current);
|
|
if (sq->pop_compare_func(data, less_equal, sq->arg) <= 0) {
|
|
fc_list_del_init(current);
|
|
} else {
|
|
data = NULL;
|
|
}
|
|
} while (0);
|
|
PTHREAD_MUTEX_UNLOCK(&sq->lcp.lock);
|
|
|
|
return data;
|
|
}
|
|
|
|
void sorted_queue_pop_to_chain_ex(struct sorted_queue *sq,
|
|
void *less_equal, struct fc_list_head *head,
|
|
const bool blocked)
|
|
{
|
|
struct fc_list_head *current;
|
|
|
|
PTHREAD_MUTEX_LOCK(&sq->lcp.lock);
|
|
do {
|
|
if (fc_list_empty(&sq->head)) {
|
|
if (!blocked) {
|
|
FC_INIT_LIST_HEAD(head);
|
|
break;
|
|
}
|
|
|
|
pthread_cond_wait(&sq->lcp.cond,
|
|
&sq->lcp.lock);
|
|
}
|
|
|
|
if (fc_list_empty(&sq->head)) {
|
|
FC_INIT_LIST_HEAD(head);
|
|
} else {
|
|
current = sq->head.next;
|
|
if (sq->pop_compare_func(FC_SORTED_QUEUE_DATA_PTR(
|
|
sq, current), less_equal, sq->arg) <= 0)
|
|
{
|
|
head->next = current;
|
|
current->prev = head;
|
|
current = current->next;
|
|
while (current != &sq->head && sq->pop_compare_func(
|
|
FC_SORTED_QUEUE_DATA_PTR(sq, current),
|
|
less_equal, sq->arg) <= 0)
|
|
{
|
|
current = current->next;
|
|
}
|
|
|
|
head->prev = current->prev;
|
|
current->prev->next = head;
|
|
if (current == &sq->head) {
|
|
FC_INIT_LIST_HEAD(&sq->head);
|
|
} else {
|
|
sq->head.next = current;
|
|
current->prev = &sq->head;
|
|
}
|
|
} else {
|
|
FC_INIT_LIST_HEAD(head);
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
PTHREAD_MUTEX_UNLOCK(&sq->lcp.lock);
|
|
}
|
|
|
|
int sorted_queue_free_chain(struct sorted_queue *sq,
|
|
struct fast_mblock_man *mblock, struct fc_list_head *head)
|
|
{
|
|
struct fast_mblock_node *previous;
|
|
struct fast_mblock_node *current;
|
|
struct fast_mblock_chain chain;
|
|
struct fc_list_head *node;
|
|
|
|
if (fc_list_empty(&sq->head)) {
|
|
return 0;
|
|
}
|
|
|
|
node = head->next;
|
|
chain.head = previous = fast_mblock_to_node_ptr(
|
|
FC_SORTED_QUEUE_DATA_PTR(sq, node));
|
|
node = node->next;
|
|
while (node != head) {
|
|
current = fast_mblock_to_node_ptr(FC_SORTED_QUEUE_DATA_PTR(sq, node));
|
|
previous->next = current;
|
|
previous = current;
|
|
node = node->next;
|
|
}
|
|
|
|
previous->next = NULL;
|
|
chain.tail = previous;
|
|
return fast_mblock_batch_free(mblock, &chain);
|
|
}
|