Skip to content

File syn_timer_wheel.c

File List > sched > syn_timer_wheel.c

Go to the documentation of this file

#include "syn_timer_wheel.h"

#include "../util/syn_assert.h"

#include <string.h>

SYN_Status syn_timer_wheel_init(SYN_TimerWheel *wheel)
{
    if (!wheel)
        return SYN_INVALID_PARAM;

    memset(wheel, 0, sizeof(*wheel));
    wheel->current_tick = 0;

    return SYN_OK;
}

SYN_Status syn_timer_wheel_add(SYN_TimerWheel *wheel, SYN_TimerWheelNode *node,
                               uint32_t delay_ticks, SYN_TimerWheelCb cb, void *arg)
{
    if (!wheel || !node || !cb || delay_ticks == 0)
        return SYN_INVALID_PARAM;

    if (node->active) {
        syn_timer_wheel_cancel(wheel, node);
    }

    uint32_t target_tick = wheel->current_tick + delay_ticks;
    uint32_t bucket_idx = target_tick % SYN_TIMER_WHEEL_BUCKETS;
    uint32_t rotations = delay_ticks / SYN_TIMER_WHEEL_BUCKETS;

    node->expires_tick = target_tick;
    node->rotation_count = rotations;
    node->cb = cb;
    node->arg = arg;
    node->active = true;
    node->next = wheel->buckets[bucket_idx];
    node->prev = NULL;

    if (wheel->buckets[bucket_idx]) {
        wheel->buckets[bucket_idx]->prev = node;
    }
    wheel->buckets[bucket_idx] = node;

    return SYN_OK;
}

SYN_Status syn_timer_wheel_cancel(SYN_TimerWheel *wheel, SYN_TimerWheelNode *node)
{
    if (!wheel || !node || !node->active)
        return SYN_INVALID_PARAM;

    uint32_t bucket_idx = node->expires_tick % SYN_TIMER_WHEEL_BUCKETS;

    if (node->prev) {
        node->prev->next = node->next;
    } else {
        wheel->buckets[bucket_idx] = node->next;
    }

    if (node->next) {
        node->next->prev = node->prev;
    }

    node->next = NULL;
    node->prev = NULL;
    node->active = false;

    return SYN_OK;
}

size_t syn_timer_wheel_step(SYN_TimerWheel *wheel)
{
    if (!wheel)
        return 0;

    wheel->current_tick++;
    uint32_t bucket_idx = wheel->current_tick % SYN_TIMER_WHEEL_BUCKETS;
    SYN_TimerWheelNode *curr = wheel->buckets[bucket_idx];
    size_t fired_count = 0;

    while (curr) {
        SYN_TimerWheelNode *next_node = curr->next;
        if (curr->rotation_count > 0) {
            curr->rotation_count--;
        } else {
            /* Unlink timer node */
            if (curr->prev) {
                curr->prev->next = curr->next;
            } else {
                wheel->buckets[bucket_idx] = curr->next;
            }
            if (curr->next) {
                curr->next->prev = curr->prev;
            }

            curr->next = NULL;
            curr->prev = NULL;
            curr->active = false;

            /* Invoke callback */
            if (curr->cb) {
                curr->cb(curr->arg);
                fired_count++;
            }
        }
        curr = next_node;
    }

    return fired_count;
}