EtherLib/timer.c

227 lines
5.9 KiB
C

//
// Created by epagris on 2022.12.21..
//
#include "timer.h"
#include "dynmem.h"
#include "utils.h"
#include <stdlib.h>
// FIXME nagyon bugos vagyok! Help me! :)
int64_t time_to_us(const TimePoint *t) {
return (int64_t)t->s * 1000000 + (int64_t)t->us;
}
void time_from_us(TimePoint *t, int64_t us) {
t->s = us / 1000000;
t->us = us - ((int64_t)t->s * 1000000);
}
void time_add_us(TimePoint *t, int64_t us) {
time_from_us(t, time_to_us(t) + us);
}
// ------------------------------
Timer *timer_new(uint32_t maxSched) {
Timer *tmr = (Timer *)dynmem_alloc(sizeof(Timer) + maxSched * sizeof(AlarmAssignment));
ASSERT_NULL(tmr);
ETHLIB_OS_MTX_CREATE(&tmr->tabMtx);
tmr->maxSched = maxSched;
tmr->nSched = 0;
tmr->nextAlarm = NULL;
tmr->time.s = 0;
tmr->time.us = 0;
memset(tmr->alarms, 0, maxSched * sizeof(AlarmAssignment));
return tmr;
}
static AlarmAssignment *timer_get_alarm_by_id(Timer *tmr, uint32_t id) {
AlarmAssignment *slot = NULL;
for (uint32_t i = 0; i < tmr->maxSched; i++) {
if (tmr->alarms[i].id == id) {
slot = (tmr->alarms) + i;
break;
}
}
return slot;
}
static uint32_t timer_generate_id(Timer *tmr) {
uint32_t newId = 0;
do {
newId = ((uint32_t)rand()) | 0x01; // ID cannot be 0
} while (timer_get_alarm_by_id(tmr, newId) != NULL);
return newId;
}
static void timer_update_nearest_alarm(Timer *tmr) {
if (tmr->nSched == 0) {
tmr->nextAlarm = NULL;
return;
}
int64_t t_c_us = time_to_us(&tmr->time);
int64_t min_delta_t_us = 0;
AlarmAssignment *nearest = NULL;
for (uint32_t i = 0; i < tmr->maxSched; i++) {
int64_t t_i_us = time_to_us(&(tmr->alarms[i].time));
if ((nearest == NULL) && (tmr->alarms[i].id != 0)) {
nearest = tmr->alarms + i;
min_delta_t_us = t_i_us - t_c_us;
} else {
int64_t delta_t_us = t_i_us - t_c_us;
if (delta_t_us < min_delta_t_us) {
min_delta_t_us = delta_t_us;
nearest = tmr->alarms + i;
}
}
}
tmr->nextAlarm = nearest;
}
uint32_t timer_sched(Timer *tmr, const TimePoint *t, TimerAlarmCb cb, AlarmUserData params) {
if (tmr->nSched == tmr->maxSched) { // if cannot schedule more alarm
return TIMER_SCHED_FAILED;
}
ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx));
// if we can schedule get the first unused block
AlarmAssignment *slot = timer_get_alarm_by_id(tmr, 0);
// allocate on the first free slot
slot->id = timer_generate_id(tmr);
slot->cb = cb;
slot->time = *t;
slot->params = params;
// increase allocation count
tmr->nSched++;
// replace nearest if needed
if (tmr->nSched > 1) {
timer_update_nearest_alarm(tmr);
} else {
tmr->nextAlarm = slot;
}
//timer_report(tmr);
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
return slot->id;
}
uint32_t timer_sched_rel(Timer *tmr, int64_t us, TimerAlarmCb cb, AlarmUserData params) {
TimePoint t = tmr->time;
time_add_us(&t, us);
return timer_sched(tmr, &t, cb, params);
}
void timer_unsched(Timer *tmr, uint32_t id) {
ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx));
if (tmr->nSched > 0) {
AlarmAssignment *alarm = timer_get_alarm_by_id(tmr, id);
if (alarm != NULL) {
memset(alarm, 0, sizeof(AlarmAssignment));
tmr->nSched--;
timer_update_nearest_alarm(tmr);
}
}
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
}
void timer_set_time(Timer *tmr, const TimePoint *t) {
tmr->time = *t;
}
int64_t timer_get_time_us(const Timer *tmr) {
return time_to_us(&tmr->time);
}
TimePoint timer_get_time(const Timer *tmr) {
return tmr->time;
}
void timer_tick(Timer *tmr, int64_t us) {
// advance time
time_add_us(&tmr->time, us);
/*t_us += us;
time_from_us(&tmr->time, t_us);*/
int64_t t_us = timer_get_time_us(tmr);
if ((tmr->nSched > 0) && (tmr->nextAlarm != NULL)) {
int64_t t_alarm = time_to_us(&(tmr->nextAlarm->time));
while (((t_alarm - t_us) <= 0) && (tmr->nSched > 0)) {
if (ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx)) != 0) {
return; // break from the loop, since we cannot lock the mutex
}
//timer_report(tmr);
// fetch alarm (obtain a COPY)
AlarmAssignment alarm = *(tmr->nextAlarm);
// clear alarm descriptor
memset(tmr->nextAlarm, 0, sizeof(AlarmAssignment));
// decrease scheduled alarm count
tmr->nSched--;
// update nearest alarm
timer_update_nearest_alarm(tmr);
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
// invoke callback
if (alarm.cb != NULL) {
alarm.cb(tmr, alarm.params);
}
if (tmr->nextAlarm != NULL) {
t_alarm = time_to_us(&(tmr->nextAlarm->time));
}
}
}
}
void timer_report(const Timer *tmr) {
MSG("Time: %u.%06u\n\n", tmr->time.s, tmr->time.us);
int64_t t_c_us = timer_get_time_us(tmr);
if (tmr->nSched > 0) {
for (uint32_t i = 0; i < tmr->maxSched; i++) {
if (tmr->alarms[i].id == 0) {
continue;
}
int64_t t_delta_us = time_to_us(&tmr->alarms[i].time) - t_c_us;
if (t_delta_us < 1000000) {
MSG("─ alarm (#%X) in %li us", tmr->alarms[i].id, t_delta_us);
} else {
TimePoint dt;
time_from_us(&dt, t_delta_us);
MSG("─ alarm (#%X) in %i.%06i s", tmr->alarms[i].id, dt.s, dt.us);
}
if ((tmr->alarms + i) == tmr->nextAlarm) {
MSG(" <-- NEXT ALARM\n");
} else {
MSG("\n");
}
}
}
MSG("\nSchedules: %u/%u\n", tmr->nSched, tmr->maxSched);
}