1 /*
2  * Copyright (c) 2014 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include "minip-internal.h"
10 
11 #include <lk/trace.h>
12 #include <lk/debug.h>
13 #include <lk/compiler.h>
14 #include <stdlib.h>
15 #include <lk/list.h>
16 #include <lk/err.h>
17 #include <sys/types.h>
18 #include <kernel/thread.h>
19 #include <kernel/event.h>
20 #include <kernel/mutex.h>
21 #include <platform.h>
22 
23 #define LOCAL_TRACE 0
24 
25 static struct list_node net_timer_list = LIST_INITIAL_VALUE(net_timer_list);
26 static event_t net_timer_event = EVENT_INITIAL_VALUE(net_timer_event, false, 0);
27 static mutex_t net_timer_lock = MUTEX_INITIAL_VALUE(net_timer_lock);
28 
add_to_queue(net_timer_t * t)29 static void add_to_queue(net_timer_t *t) {
30     net_timer_t *e;
31     list_for_every_entry(&net_timer_list, e, net_timer_t, node) {
32         if (TIME_GT(e->sched_time, t->sched_time)) {
33             list_add_before(&e->node, &t->node);
34             return;
35         }
36     }
37 
38     list_add_tail(&net_timer_list, &t->node);
39 }
40 
net_timer_set(net_timer_t * t,net_timer_callback_t cb,void * callback_args,lk_time_t delay)41 bool net_timer_set(net_timer_t *t, net_timer_callback_t cb, void *callback_args, lk_time_t delay) {
42     bool newly_queued = true;
43 
44     lk_time_t now = current_time();
45 
46     mutex_acquire(&net_timer_lock);
47 
48     if (list_in_list(&t->node)) {
49         list_delete(&t->node);
50         newly_queued = false;
51     }
52 
53     t->cb = cb;
54     t->arg = callback_args;
55     t->sched_time = now + delay;
56 
57     add_to_queue(t);
58 
59     mutex_release(&net_timer_lock);
60 
61     event_signal(&net_timer_event, true);
62 
63     return newly_queued;
64 }
65 
net_timer_cancel(net_timer_t * t)66 bool net_timer_cancel(net_timer_t *t) {
67     bool was_queued = false;
68 
69     mutex_acquire(&net_timer_lock);
70 
71     if (list_in_list(&t->node)) {
72         list_delete(&t->node);
73         was_queued = true;
74     }
75 
76     mutex_release(&net_timer_lock);
77 
78     return was_queued;
79 }
80 
81 /* returns the delay to the next event */
net_timer_work_routine(void)82 static lk_time_t net_timer_work_routine(void) {
83     lk_time_t now = current_time();
84     lk_time_t delay = INFINITE_TIME;
85 
86     mutex_acquire(&net_timer_lock);
87 
88     for (;;) {
89         net_timer_t *e;
90         e = list_peek_head_type(&net_timer_list, net_timer_t, node);
91         if (!e) {
92             delay = INFINITE_TIME;
93             goto done;
94         }
95 
96         if (TIME_GT(e->sched_time, now)) {
97             delay = e->sched_time - now;
98             goto done;
99         }
100 
101         list_delete(&e->node);
102 
103         mutex_release(&net_timer_lock);
104 
105         LTRACEF("firing timer %p, cb %p, arg %p\n", e, e->cb, e->arg);
106         e->cb(e->arg);
107 
108         mutex_acquire(&net_timer_lock);
109     }
110 
111 done:
112     if (delay == INFINITE_TIME)
113         event_unsignal(&net_timer_event);
114 
115     mutex_release(&net_timer_lock);
116 
117     return delay;
118 }
119 
net_timer_work_thread(void * args)120 static int net_timer_work_thread(void *args) {
121     for (;;) {
122         event_wait(&net_timer_event);
123 
124         lk_time_t delay = net_timer_work_routine();
125         if (delay != INFINITE_TIME) {
126             thread_sleep(MIN(delay, 100));
127         }
128     }
129 
130     return 0;
131 }
132 
net_timer_init(void)133 void net_timer_init(void) {
134     thread_detach_and_resume(thread_create("net timer", &net_timer_work_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
135 }
136 
137 
138