/* * Copyright 2017, Data61 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) * ABN 41 687 119 230. * * This software may be distributed and modified according to the terms of * the BSD 2-Clause license. Note that NO WARRANTY is provided. * See "LICENSE_BSD2.txt" for details. * * @TAG(DATA61_BSD) */ #include #include 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; }