aboutsummaryrefslogtreecommitdiff
path: root/kernel/util/timer.c
blob: f1be4a22ca1abe4e0e2d592c9de3a70a52da5a19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "util/timer.h"
#include "proc/spinlock.h"
#include "util/time.h"

static timer_t *timer_running = NULL;
static uint64_t timer_next_expiry = -1;
static list_t timers_primary = LIST_INITIALIZER(timers_primary);
static list_t timers_secondary = LIST_INITIALIZER(timers_secondary);
static int timers_firing = 0;

void timer_init(timer_t *timer)
{
    timer->expires = -1;
    list_link_init(&timer->link);
}

void timer_add(timer_t *timer) { timer_mod(timer, timer->expires); }

int __timer_del(timer_t *timer)
{
    int ret = 0;
    if (list_link_is_linked(&timer->link))
    {
        list_remove(&timer->link);
        ret = 1;
    }
    return ret;
}

int timer_del(timer_t *timer)
{
    int ret = __timer_del(timer);

    return ret;
}

void __timer_add(timer_t *timer)
{
    KASSERT(!list_link_is_linked(&timer->link));
    list_t *list = timers_firing ? &timers_secondary : &timers_primary;
    list_insert_head(list, &timer->link);
}

int timer_mod(timer_t *timer, int expires)
{

    timer->expires = expires;
    int ret = __timer_del(timer);
    __timer_add(timer);
    timer_next_expiry = MIN(timer_next_expiry, timer->expires);

    return ret;
}

int timer_pending(timer_t *timer)
{
    int ret = list_link_is_linked(&timer->link);
    return ret;
}

int timer_del_sync(timer_t *timer)
{
    /* Not great performance wise... */
    while (timer_running == timer)
    {
        sched_yield();
    }

    int ret = __timer_del(timer);

    return ret;
}

/* Note: using a linked-list rather than some priority is terribly inefficient
 * Also this implementation is just bad. Sorry.
 */
int ready = 0;
void __timers_fire()
{
    if (curthr && !preemption_enabled())
    {
        return;
    }

    timers_firing = 1;

    //dbg(DBG_PRINT, "next expiry: %d\n", timer_next_expiry);
    if (jiffies < timer_next_expiry)
    {
        timers_firing = 0;
        return;
    }

    uint64_t min_expiry = 0;

    list_iterate(&timers_primary, timer, timer_t, link)
    {
        if (jiffies >= timer->expires)
        {
            list_remove(&timer->link);
            timer_running = timer;
            timer->function(timer->data);
            timer_running = NULL;
        }
        else
        {
            min_expiry = MIN(min_expiry, timer->expires);
        }
    }

    /* migrate from the backup list to the primary list */
    list_iterate(&timers_secondary, timer, timer_t, link)
    {
        min_expiry = MIN(min_expiry, timer->expires);
        list_remove(&timer->link);
        list_insert_head(&timers_primary, &timer->link);
    }

    timer_next_expiry = min_expiry;
    timers_firing = 0;
}