blob: 50e6b9c696cc0f2d78abc79d03f7116694509675 [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <utils/sglib.h>
#include <platsupport/tqueue.h>
static int cmp(uint64_t a, uint64_t b)
{
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
#define TIMEOUT_CMP(t1, t2) (cmp(t1->timeout.abs_time, t2->timeout.abs_time))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
SGLIB_DEFINE_SORTED_LIST_PROTOTYPES(tqueue_node_t, TIMEOUT_CMP, next)
SGLIB_DEFINE_SORTED_LIST_FUNCTIONS(tqueue_node_t, TIMEOUT_CMP, next)
#pragma GCC diagnostic pop
static tqueue_node_t *head(tqueue_node_t *list)
{
struct sglib_tqueue_node_t_iterator it;
return sglib_tqueue_node_t_it_init(&it, list);
}
int tqueue_alloc_id(tqueue_t *tq, unsigned int *id)
{
if (!tq || !id) {
return EINVAL;
}
for (int i = 0; i < tq->n; i++) {
if (!tq->array[i].allocated) {
tq->array[i].allocated = true;
*id = i;
return 0;
}
}
ZF_LOGE("Out of timer client ids\n");
return ENOMEM;
}
int tqueue_alloc_id_at(tqueue_t *tq, unsigned int id)
{
if (!tq || id >= tq->n) {
return EINVAL;
}
if (tq->array[id].allocated) {
return EADDRINUSE;
}
tq->array[id].allocated = true;
return 0;
}
int tqueue_free_id(tqueue_t *tq, unsigned int id)
{
if (!tq) {
return EINVAL;
}
if (id < 0 || id >= tq->n) {
ZF_LOGE("Invalid id");
return EINVAL;
}
if (!tq->array[id].allocated) {
ZF_LOGW("Freeing unallocated id");
return EINVAL;
}
/* remove from queue */
if (tq->array[id].active) {
sglib_tqueue_node_t_delete(&tq->queue, &tq->array[id]);
tq->array[id].active = false;
}
tq->array[id].allocated = false;
return 0;
}
int tqueue_register(tqueue_t *tq, unsigned int id, timeout_t *timeout)
{
if (!tq) {
return EINVAL;
}
if (id < 0 || id > tq->n || !tq->array[id].allocated) {
ZF_LOGE("invalid id");
return EINVAL;
}
/* delete the callback from the queue if its present */
if (tq->array[id].active) {
sglib_tqueue_node_t_delete(&tq->queue, &tq->array[id]);
}
/* update node */
tq->array[id].active = true;
tq->array[id].timeout = *timeout;
/* add to data structure */
sglib_tqueue_node_t_add(&tq->queue, &tq->array[id]);
return 0;
}
int tqueue_cancel(tqueue_t *tq, unsigned int id) {
if (!tq) {
return EINVAL;
}
/* iterate through the list until we find that id */
if (id < 0 || id > tq->n) {
ZF_LOGE("Invalid id");
return EINVAL;
}
/* delete the callback from the queue if its present */
if (tq->array[id].active) {
sglib_tqueue_node_t_delete(&tq->queue, &tq->array[id]);
}
tq->array[id].active = false;
return 0;
}
int tqueue_update(tqueue_t *tq, uint64_t curr_time, uint64_t *next_time) {
if (!tq) {
return EINVAL;
}
/* keep checking the head of this queue */
tqueue_node_t *t = head(tq->queue);
while (t != NULL && t->timeout.abs_time <= curr_time) {
if (t->active) {
t->timeout.callback(t->timeout.token);
}
/* check if it is active again, as callback may have deactivated the timeout */
if (t->active) {
sglib_tqueue_node_t_delete(&tq->queue, t);
if (t->timeout.period > 0) {
t->timeout.abs_time += t->timeout.period;
sglib_tqueue_node_t_add(&tq->queue, t);
} else {
t->active = false;
}
}
t = head(tq->queue);
}
if (next_time) {
if (t) {
*next_time = t->timeout.abs_time;
} else {
*next_time = 0;
}
}
return 0;
}
int tqueue_init_static(tqueue_t *tq, ps_malloc_ops_t *mops, int size)
{
if (!tq || !mops) {
return EINVAL;
}
if (size <= 0) {
return EINVAL;
}
/* initialise the list */
tq->n = size;
int error = ps_calloc(mops, size, sizeof(tqueue_node_t), (void **) &tq->array);
if (error) {
return ENOMEM;
}
assert(tq->array != NULL);
/* noone currently in the queue */
tq->queue = NULL;
return 0;
}