mirror of
https://github.com/nyanmisaka/mpp.git
synced 2025-10-05 17:16:50 +08:00

Refactor thread, list, queue and time module. Signed-off-by: Chandler Chen <chandler.chen@rock-chips.com> Signed-off-by: Hongjin Li <vic.hong@rock-chips.com> Signed-off-by: Herman Chen <herman.chen@rock-chips.com> Change-Id: I96c07e1549868085867502c8bb974ffd3875ea9d
599 lines
14 KiB
C
599 lines
14 KiB
C
/* SPDX-License-Identifier: Apache-2.0 OR MIT */
|
|
/*
|
|
* Copyright (c) 2015 Rockchip Electronics Co., Ltd.
|
|
*/
|
|
|
|
#define MODULE_TAG "mpp_list"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
|
|
#include "mpp_mem.h"
|
|
#include "mpp_list.h"
|
|
#include "mpp_debug.h"
|
|
#include "mpp_common.h"
|
|
|
|
#define LIST_DEBUG(fmt, ...) mpp_log(fmt, ## __VA_ARGS__)
|
|
#define LIST_ERROR(fmt, ...) mpp_err(fmt, ## __VA_ARGS__)
|
|
|
|
static inline void list_node_init(MppListNode *node)
|
|
{
|
|
node->prev = node->next = node;
|
|
}
|
|
|
|
static inline void list_node_init_with_key_and_size(MppListNode *node, rk_u32 key, rk_s32 size)
|
|
{
|
|
list_node_init(node);
|
|
node->key = key;
|
|
node->size = size;
|
|
}
|
|
|
|
static MppListNode* create_list(void *data, rk_s32 size, rk_u32 key)
|
|
{
|
|
MppListNode *node = mpp_malloc_size(MppListNode, sizeof(MppListNode) + size);
|
|
|
|
if (node) {
|
|
void *dst = (void*)(node + 1);
|
|
list_node_init_with_key_and_size(node, key, size);
|
|
memcpy(dst, data, size);
|
|
} else {
|
|
LIST_ERROR("failed to allocate list node");
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static inline void _mpp_list_add(MppListNode * _new, MppListNode * prev, MppListNode * next)
|
|
{
|
|
next->prev = _new;
|
|
_new->next = next;
|
|
_new->prev = prev;
|
|
prev->next = _new;
|
|
}
|
|
|
|
static inline void mpp_list_add(MppListNode *_new, MppListNode *head)
|
|
{
|
|
_mpp_list_add(_new, head, head->next);
|
|
}
|
|
|
|
static inline void mpp_list_add_tail(MppListNode *_new, MppListNode *head)
|
|
{
|
|
_mpp_list_add(_new, head->prev, head);
|
|
}
|
|
|
|
int mpp_list_add_at_head(MppList *list, void *data, int size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list->head) {
|
|
MppListNode *node = create_list(data, size, 0);
|
|
if (node) {
|
|
mpp_list_add(node, list->head);
|
|
list->count++;
|
|
ret = 0;
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int mpp_list_add_at_tail(MppList *list, void *data, int size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list->head) {
|
|
MppListNode *node = create_list(data, size, 0);
|
|
|
|
if (node) {
|
|
mpp_list_add_tail(node, list->head);
|
|
list->count++;
|
|
ret = 0;
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void release_list(MppListNode*node, void *data, rk_s32 size)
|
|
{
|
|
void *src = (void*)(node + 1);
|
|
|
|
if (node->size == size) {
|
|
if (data)
|
|
memcpy(data, src, size);
|
|
} else {
|
|
LIST_ERROR("node size check failed when release_list");
|
|
size = (size < node->size) ? (size) : (node->size);
|
|
if (data)
|
|
memcpy(data, src, size);
|
|
}
|
|
mpp_free(node);
|
|
}
|
|
|
|
static inline void _mpp_list_del(MppListNode *prev, MppListNode *next)
|
|
{
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
}
|
|
|
|
static inline void mpp_list_del_init(MppListNode *node)
|
|
{
|
|
_mpp_list_del(node->prev, node->next);
|
|
list_node_init(node);
|
|
}
|
|
|
|
static inline void _list_del_node_no_lock(MppListNode *node, void *data, rk_s32 size)
|
|
{
|
|
mpp_list_del_init(node);
|
|
release_list(node, data, size);
|
|
}
|
|
|
|
int mpp_list_del_at_head(MppList *list, void *data, int size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list->head && list->count) {
|
|
_list_del_node_no_lock(list->head->next, data, size);
|
|
list->count--;
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int mpp_list_del_at_tail(MppList *list, void *data, int size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list->head && list->count) {
|
|
_list_del_node_no_lock(list->head->prev, data, size);
|
|
list->count--;
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
static MppListNode* create_list_with_size(void *data, rk_s32 size, rk_u32 key)
|
|
{
|
|
MppListNode *node = mpp_malloc_size(MppListNode, sizeof(MppListNode) + sizeof(size) + size);
|
|
|
|
if (node) {
|
|
rk_s32 *dst = (rk_s32 *)(node + 1);
|
|
list_node_init_with_key_and_size(node, key, size);
|
|
*dst++ = size;
|
|
memcpy(dst, data, size);
|
|
} else {
|
|
LIST_ERROR("failed to allocate list node");
|
|
}
|
|
return node;
|
|
}
|
|
|
|
rk_s32 mpp_list_fifo_wr(MppList *list, void *data, rk_s32 size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list && list->head) {
|
|
MppListNode *node = create_list_with_size(data, size, 0);
|
|
|
|
if (node) {
|
|
mpp_list_add_tail(node, list->head);
|
|
list->count++;
|
|
ret = 0;
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void release_list_with_size(MppListNode* node, void *data, rk_s32 *size)
|
|
{
|
|
rk_s32 *src = (rk_s32*)(node + 1);
|
|
rk_s32 data_size = *src++;
|
|
|
|
*size = data_size;
|
|
|
|
if (data)
|
|
memcpy(data, src, data_size);
|
|
|
|
mpp_free(node);
|
|
}
|
|
|
|
rk_s32 mpp_list_fifo_rd(MppList *list, void *data, rk_s32 *size)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
if (list && list->head && list->count) {
|
|
MppListNode *node = list->head->next;
|
|
|
|
mpp_list_del_init(node);
|
|
release_list_with_size(node, data, size);
|
|
list->count--;
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int mpp_list_is_empty(MppList *list)
|
|
{
|
|
return list->count == 0;
|
|
}
|
|
|
|
int mpp_list_size(MppList *list)
|
|
{
|
|
return list->count;
|
|
}
|
|
|
|
rk_s32 mpp_list_add_by_key(MppList *list, void *data, rk_s32 size, rk_u32 *key)
|
|
{
|
|
rk_s32 ret = 0;
|
|
|
|
if (list->head) {
|
|
MppListNode *node;
|
|
rk_u32 list_key = mpp_list_get_key(list);
|
|
|
|
*key = list_key;
|
|
node = create_list(data, size, list_key);
|
|
if (node) {
|
|
mpp_list_add_tail(node, list->head);
|
|
list->count++;
|
|
ret = 0;
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
rk_s32 mpp_list_del_by_key(MppList *list, void *data, rk_s32 size, rk_u32 key)
|
|
{
|
|
rk_s32 ret = 0;
|
|
|
|
if (list && list->head && list->count) {
|
|
MppListNode *tmp = list->head->next;
|
|
|
|
ret = -EINVAL;
|
|
while (tmp->next != list->head) {
|
|
if (tmp->key == key) {
|
|
_list_del_node_no_lock(tmp, data, size);
|
|
list->count--;
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
rk_s32 mpp_list_show_by_key(MppList *list, void *data, rk_u32 key)
|
|
{
|
|
rk_s32 ret = -EINVAL;
|
|
|
|
(void)list;
|
|
(void)data;
|
|
(void)key;
|
|
return ret;
|
|
}
|
|
|
|
void mpp_list_flush(MppList* list)
|
|
{
|
|
if (list->head) {
|
|
while (list->count) {
|
|
MppListNode* node = list->head->next;
|
|
|
|
mpp_list_del_init(node);
|
|
|
|
if (list->destroy) {
|
|
list->destroy((void*)(node + 1));
|
|
}
|
|
|
|
mpp_free(node);
|
|
list->count--;
|
|
}
|
|
}
|
|
|
|
mpp_list_signal(list);
|
|
}
|
|
|
|
MPP_RET mpp_list_wait(MppList* list)
|
|
{
|
|
int ret;
|
|
|
|
ret = mpp_mutex_cond_wait(&list->cond_lock);
|
|
|
|
if (ret == 0) {
|
|
return MPP_OK;
|
|
} else if (ret == ETIMEDOUT) {
|
|
return MPP_NOK;
|
|
} else {
|
|
return MPP_NOK;
|
|
}
|
|
}
|
|
|
|
MPP_RET mpp_list_wait_timed(MppList *list, rk_s64 timeout)
|
|
{
|
|
int ret;
|
|
|
|
ret = (MPP_RET)mpp_mutex_cond_timedwait(&list->cond_lock, timeout);
|
|
|
|
if (ret == 0) {
|
|
return MPP_OK;
|
|
} else if (ret == ETIMEDOUT) {
|
|
return MPP_NOK;
|
|
} else {
|
|
return MPP_NOK;
|
|
}
|
|
}
|
|
|
|
MPP_RET mpp_list_wait_lt(MppList *list, rk_s64 timeout, rk_s32 val)
|
|
{
|
|
if (list->count < val)
|
|
return MPP_OK;
|
|
|
|
if (timeout < 0) {
|
|
return mpp_list_wait(list);
|
|
} else {
|
|
return mpp_list_wait_timed(list, timeout);
|
|
}
|
|
}
|
|
|
|
MPP_RET mpp_list_wait_le(MppList *list, rk_s64 timeout, rk_s32 val)
|
|
{
|
|
if (list->count <= val)
|
|
return MPP_OK;
|
|
|
|
if (timeout < 0) {
|
|
return mpp_list_wait(list);
|
|
} else {
|
|
return mpp_list_wait_timed(list, timeout);
|
|
}
|
|
}
|
|
|
|
MPP_RET mpp_list_wait_gt(MppList *list, rk_s64 timeout, rk_s32 val)
|
|
{
|
|
if (list->count > val)
|
|
return MPP_OK;
|
|
|
|
if (timeout < 0) {
|
|
return mpp_list_wait(list);
|
|
} else {
|
|
return mpp_list_wait_timed(list, timeout);
|
|
}
|
|
}
|
|
|
|
MPP_RET mpp_list_wait_ge(MppList *list, rk_s64 timeout, rk_s32 val)
|
|
{
|
|
if (list->count >= val)
|
|
return MPP_OK;
|
|
|
|
if (timeout < 0) {
|
|
return mpp_list_wait(list);
|
|
} else {
|
|
return mpp_list_wait_timed(list, timeout);
|
|
}
|
|
}
|
|
|
|
void mpp_list_signal(MppList *list)
|
|
{
|
|
mpp_mutex_cond_signal(&list->cond_lock);
|
|
}
|
|
|
|
rk_u32 mpp_list_get_key(MppList *list)
|
|
{
|
|
return list->keys++;
|
|
}
|
|
|
|
MppList *mpp_list_create(node_destructor func)
|
|
{
|
|
MppList *list = mpp_malloc(MppList, 1);
|
|
|
|
if (list == NULL) {
|
|
LIST_ERROR("Failed to allocate memory for mpp_list.\n");
|
|
return NULL;
|
|
}
|
|
|
|
list->destroy = func;
|
|
list->count = 0;
|
|
|
|
list->head = mpp_malloc(MppListNode, 1);
|
|
if (list->head == NULL) {
|
|
LIST_ERROR("Failed to allocate memory for list header.\n");
|
|
mpp_free(list);
|
|
return NULL;
|
|
}
|
|
|
|
list_node_init_with_key_and_size(list->head, 0, 0);
|
|
|
|
mpp_mutex_cond_init(&list->cond_lock);
|
|
|
|
return list;
|
|
}
|
|
|
|
void mpp_list_destroy(MppList *list)
|
|
{
|
|
MppListNode *node;
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
mpp_list_flush(list);
|
|
|
|
node = list->head->next;
|
|
while (node != list->head) {
|
|
MppListNode *next = node->next;
|
|
|
|
mpp_free(node);
|
|
node = next;
|
|
}
|
|
|
|
mpp_mutex_cond_destroy(&list->cond_lock);
|
|
|
|
mpp_free(list->head);
|
|
list->head = NULL;
|
|
|
|
mpp_free(list);
|
|
list = NULL;
|
|
}
|
|
|
|
/* list sort porting from kernel list_sort.c */
|
|
|
|
/*
|
|
* Returns a list organized in an intermediate format suited
|
|
* to chaining of merge() calls: null-terminated, no reserved or
|
|
* sentinel head node, "prev" links not maintained.
|
|
*/
|
|
static struct list_head *merge(void *priv, ListCmpFunc cmp,
|
|
struct list_head *a, struct list_head *b)
|
|
{
|
|
struct list_head *head, **tail = &head;
|
|
|
|
for (;;) {
|
|
/* if equal, take 'a' -- important for sort stability */
|
|
if (cmp(priv, a, b) <= 0) {
|
|
*tail = a;
|
|
tail = &a->next;
|
|
a = a->next;
|
|
if (!a) {
|
|
*tail = b;
|
|
break;
|
|
}
|
|
} else {
|
|
*tail = b;
|
|
tail = &b->next;
|
|
b = b->next;
|
|
if (!b) {
|
|
*tail = a;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return head;
|
|
}
|
|
|
|
/*
|
|
* Combine final list merge with restoration of standard doubly-linked
|
|
* list structure. This approach duplicates code from merge(), but
|
|
* runs faster than the tidier alternatives of either a separate final
|
|
* prev-link restoration pass, or maintaining the prev links
|
|
* throughout.
|
|
*/
|
|
static void merge_final(void *priv, ListCmpFunc cmp, struct list_head *head,
|
|
struct list_head *a, struct list_head *b)
|
|
{
|
|
struct list_head *tail = head;
|
|
rk_u8 count = 0;
|
|
|
|
for (;;) {
|
|
/* if equal, take 'a' -- important for sort stability */
|
|
if (cmp(priv, a, b) <= 0) {
|
|
tail->next = a;
|
|
a->prev = tail;
|
|
tail = a;
|
|
a = a->next;
|
|
if (!a)
|
|
break;
|
|
} else {
|
|
tail->next = b;
|
|
b->prev = tail;
|
|
tail = b;
|
|
b = b->next;
|
|
if (!b) {
|
|
b = a;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finish linking remainder of list b on to tail */
|
|
tail->next = b;
|
|
do {
|
|
/*
|
|
* If the merge is highly unbalanced (e.g. the input is
|
|
* already sorted), this loop may run many iterations.
|
|
* Continue callbacks to the client even though no
|
|
* element comparison is needed, so the client's cmp()
|
|
* routine can invoke cond_resched() periodically.
|
|
*/
|
|
if (!++count)
|
|
cmp(priv, b, b);
|
|
b->prev = tail;
|
|
tail = b;
|
|
b = b->next;
|
|
} while (b);
|
|
|
|
/* And the final links to make a circular doubly-linked list */
|
|
tail->next = head;
|
|
head->prev = tail;
|
|
}
|
|
|
|
void list_sort(void *priv, struct list_head *head, ListCmpFunc cmp)
|
|
{
|
|
struct list_head *list = head->next, *pending = NULL;
|
|
size_t count = 0; /* Count of pending */
|
|
|
|
if (list == head->prev) /* Zero or one elements */
|
|
return;
|
|
|
|
/* Convert to a null-terminated singly-linked list. */
|
|
head->prev->next = NULL;
|
|
|
|
/*
|
|
* Data structure invariants:
|
|
* - All lists are singly linked and null-terminated; prev
|
|
* pointers are not maintained.
|
|
* - pending is a prev-linked "list of lists" of sorted
|
|
* sublists awaiting further merging.
|
|
* - Each of the sorted sublists is power-of-two in size.
|
|
* - Sublists are sorted by size and age, smallest & newest at front.
|
|
* - There are zero to two sublists of each size.
|
|
* - A pair of pending sublists are merged as soon as the number
|
|
* of following pending elements equals their size (i.e.
|
|
* each time count reaches an odd multiple of that size).
|
|
* That ensures each later final merge will be at worst 2:1.
|
|
* - Each round consists of:
|
|
* - Merging the two sublists selected by the highest bit
|
|
* which flips when count is incremented, and
|
|
* - Adding an element from the input as a size-1 sublist.
|
|
*/
|
|
do {
|
|
size_t bits;
|
|
struct list_head **tail = &pending;
|
|
|
|
/* Find the least-significant clear bit in count */
|
|
for (bits = count; bits & 1; bits >>= 1)
|
|
tail = &(*tail)->prev;
|
|
/* Do the indicated merge */
|
|
if (bits) {
|
|
struct list_head *a = *tail, *b = a->prev;
|
|
|
|
a = merge(priv, cmp, b, a);
|
|
/* Install the merged result in place of the inputs */
|
|
a->prev = b->prev;
|
|
*tail = a;
|
|
}
|
|
|
|
/* Move one element from input list to pending */
|
|
list->prev = pending;
|
|
pending = list;
|
|
list = list->next;
|
|
pending->next = NULL;
|
|
count++;
|
|
} while (list);
|
|
|
|
/* End of input; merge together all the pending lists. */
|
|
list = pending;
|
|
pending = pending->prev;
|
|
for (;;) {
|
|
struct list_head *next = pending->prev;
|
|
|
|
if (!next)
|
|
break;
|
|
list = merge(priv, cmp, pending, list);
|
|
pending = next;
|
|
}
|
|
/* The final merge, rebuilding prev links */
|
|
merge_final(priv, cmp, head, pending, list);
|
|
}
|